Project

General

Profile

issue_8.txt

Sergey Ivanovskiy, 05/22/2026 06:24 AM

Download (5.53 KB)

 
1
ISSUE DESCRIPTION:
2
*[MAJOR]* _functional_ @SsoReauth@.@handleSsoReauth@: postMessage contract has no nonce or session identifier — any frame on @serverOrigin@ (e.g. compromised iframe, XSS) can fake @{type:'sso-reauth-success'}@ and dismiss the overlay without an actual re-auth. Include a server-issued state/nonce that ties the message to the in-progress reauth.
3

    
4
PROPOSED FIX:
5
Have the server generate a cryptographic nonce when issuing @MSG_SSO_REAUTH@, embed it as the OIDC @state@ on the @reauthUrl@ and validate it in @handleReauthCallback@; emit the same nonce back to the opener inside the postMessage payload (and never use @'*'@ as targetOrigin). On the client, capture the nonce from @MSG_SSO_REAUTH@, validate @event.data.nonce === expectedNonce@ before treating any message as authoritative, and reject mismatches.
6

    
7
CHANGES:
8
<file: src/com/goldencode/p2j/main/VirtualDesktopWebHandler.java>
9

    
10
Before (lines 320-336, inside handleReauthCallback):
11

    
12
      boolean success = false;
13
      SsoAuthenticator ssoAuth = SecurityManager.getInstance().ssoSm.getSsoAuthenticator();
14
      if (code != null && !code.isEmpty() && ssoAuth instanceof OidcSsoAuthenticator)
15
      {
16
         OidcSsoAuthenticator oidc = (OidcSsoAuthenticator) ssoAuth;
17
         SsoAuthenticator.Result result = oidc.processReauthCallback(code, state);
18
         success = (result != null && result.isSuccess());
19
      }
20

    
21
      String msgType = success ? "sso-reauth-success" : "sso-reauth-failed";
22
      String html = "<!DOCTYPE html><html><body><script>" +
23
                    "(function(){" +
24
                    "try{window.opener.postMessage({type:'" + msgType + "'},'*');}catch(e){}" +
25
                    "window.close();" +
26
                    "})();" +
27
                    "</script></body></html>";
28

    
29
After:
30

    
31
      boolean success = false;
32
      String nonce = null;
33
      SsoAuthenticator ssoAuth = SecurityManager.getInstance().ssoSm.getSsoAuthenticator();
34
      if (code != null && !code.isEmpty() && ssoAuth instanceof OidcSsoAuthenticator)
35
      {
36
         OidcSsoAuthenticator oidc = (OidcSsoAuthenticator) ssoAuth;
37
         // processReauthCallback already validates the OIDC state -- the
38
         // nonce we round-trip below is the same value, also issued by the
39
         // server when MSG_SSO_REAUTH was pushed to the client.
40
         SsoAuthenticator.Result result = oidc.processReauthCallback(code, state);
41
         success = (result != null && result.isSuccess());
42
         if (success)
43
         {
44
            nonce = state; // safe: validated server-side
45
         }
46
      }
47

    
48
      String msgType = success ? "sso-reauth-success" : "sso-reauth-failed";
49
      // Restrict targetOrigin to the server's own origin (never '*') and
50
      // include the nonce so the opener can bind this message to the in-flight
51
      // reauth and reject forged messages from same-origin iframes / XSS.
52
      String origin = request.getHttpURI().getScheme() + "://" + request.getHttpURI().getHost()
53
         + (request.getHttpURI().getPort() > 0 ? ":" + request.getHttpURI().getPort() : "");
54
      String nonceJs = (nonce == null) ? "null"
55
         : "'" + nonce.replace("\\", "\\\\").replace("'", "\\'") + "'";
56
      String html = "<!DOCTYPE html><html><body><script>" +
57
                    "(function(){" +
58
                    "try{window.opener.postMessage(" +
59
                       "{type:'" + msgType + "',nonce:" + nonceJs + "}," +
60
                       "'" + origin + "');}catch(e){}" +
61
                    "window.close();" +
62
                    "})();" +
63
                    "</script></body></html>";
64

    
65
<file: src/com/goldencode/p2j/ui/client/driver/web/res/p2j.sso_reauth.js>
66

    
67
Before (around lines 134-148 and 268-300):
68

    
69
   function handleSsoReauth(reauthUrl, reauthTimeoutSec)
70
   {
71
      ...
72
      let serverOrigin = window.location.origin;
73
      try { serverOrigin = new URL(p2j.socket.getLogoutPage()).origin; } catch(e) {}
74

    
75
      cleanupReauthUi(false);
76
      ...
77

    
78
         reauthMessageListener = function(event)
79
         {
80
            if (event.origin !== serverOrigin)
81
            {
82
               return;
83
            }
84
            if (!event.data || typeof event.data.type !== 'string')
85
            {
86
               return;
87
            }
88

    
89
            if (event.data.type === 'sso-reauth-success')
90
            {
91

    
92
After:
93

    
94
   function handleSsoReauth(reauthUrl, reauthTimeoutSec, reauthNonce)
95
   {
96
      ...
97
      let serverOrigin = window.location.origin;
98
      try { serverOrigin = new URL(p2j.socket.getLogoutPage()).origin; } catch(e) {}
99

    
100
      // Nonce binds postMessage events to this specific reauth attempt.
101
      var expectedNonce = (typeof reauthNonce === 'string' && reauthNonce.length > 0)
102
         ? reauthNonce : null;
103

    
104
      cleanupReauthUi(false);
105
      ...
106

    
107
         reauthMessageListener = function(event)
108
         {
109
            if (event.origin !== serverOrigin)
110
            {
111
               return;
112
            }
113
            if (!event.data || typeof event.data.type !== 'string')
114
            {
115
               return;
116
            }
117
            // Require nonce match; reject any same-origin spoofs (iframes/XSS).
118
            if (expectedNonce && event.data.nonce !== expectedNonce)
119
            {
120
               console.warn('SSO re-auth: ignoring message with mismatched nonce');
121
               return;
122
            }
123

    
124
            if (event.data.type === 'sso-reauth-success')
125
            {
126

    
127
Caller (p2j.socket.js, where MSG_SSO_REAUTH dispatches to ssoReauth.handleReauth) must pass the nonce delivered with the message; the server must include @nonce@ in the @MSG_SSO_REAUTH@ payload (same value also used as the OIDC @state@ on @reauthUrl@).