apereo / phpCAS

Apereo PHP CAS Client
https://apereo.github.io/phpCAS/
Apache License 2.0
796 stars 397 forks source link

setFixedServiceURL() prevents getURL() from removing the QS parameters #181

Closed jbousquie closed 8 years ago

jbousquie commented 8 years ago

My CAS client webapp (redirect.php) is behind a reverse proxy. Its public URL is different from its internal one. Moreover, the public URL is under https but its internal is under http.

In brief, getURL() can't guess the right public URL to validate the ST, so I set the client webapp public URL with phpCAS::setFixedURL($public_URL_value).

Since the requested public URL has some parameters in its query string, it is slightly different from the fixed URL set. Example : requested : https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=eughvlgv fixed : https://ego.iut-rodez.fr/caslogger/redirect.php

It seems that, if a fixed URL is set, getURL() doesn't remove the query string what then prevents the ST to be validated.

Log example :

1CD0 .=> phpCAS::setFixedServiceURL('https://ego.iut-rodez.fr/caslogger/redirect.php') [redirect.php:30]
1CD0 .<= ''
1CD0 .=> phpCAS::setNoCasServerValidation() [redirect.php:31]
1CD0 .|    You have configured no validation of the legitimacy of the cas server. This is not recommended for production use. [CAS.php:1669]
1CD0 .<= ''
1CD0 .=> phpCAS::checkAuthentication() [redirect.php:32]
1CD0 .|    => CAS_Client::checkAuthentication() [CAS.php:1078]
1CD0 .|    |    => CAS_Client::isAuthenticated() [Client.php:1131]
1CD0 .|    |    |    => CAS_Client::_wasPreviouslyAuthenticated() [Client.php:1189]
1CD0 .|    |    |    |    no user found [Client.php:1375]
1CD0 .|    |    |    <= false
1CD0 .|    |    |    CAS 2.0 ticket `ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz' is present [Client.php:1223]
1CD0 .|    |    |    => CAS_Client::validateCAS20('', NULL, NULL) [Client.php:1224]
1CD0 .|    |    |    |     [Client.php:2747]
1CD0 .|    |    |    |    => CAS_Client::getServerServiceValidateURL() [Client.php:2753]
1CD0 .|    |    |    |    |    => CAS_Client::getURL() [Client.php:417]
1CD0 .|    |    |    |    |    <= 'https://ego.iut-rodez.fr/caslogger/redirect.php'
1CD0 .|    |    |    |    <= 'https://cas2.iut-rodez.fr/cas/serviceValidate?service=https%3A%2F%2Fego.iut-rodez.fr%2Fcaslogger%2Fredirect.php'
1CD0 .|    |    |    |    => CAS_Client::_readURL('https://cas2.iut-rodez.fr/cas/serviceValidate?service=https%3A%2F%2Fego.iut-rodez.fr%2Fcaslogger%2Fredirect.php&ticket=ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz', NULL, NULL, NULL) [Client.php:2762]
1CD0 .|    |    |    |    |    => CAS_Request_CurlRequest::sendRequest() [AbstractRequest.php:220]
1CD0 .|    |    |    |    |    |    Response Body: 
1CD0 .|    |    |    |    |    |    <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
1CD0 .|    |    |    |    |    |        <cas:authenticationFailure code='INVALID_SERVICE'>
1CD0 .|    |    |    |    |    |            ticket &#039;ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz&#039; does not match supplied service.  The original service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&amp;_cLs=http://www.iut-rodez.fr/&#039; and the supplied service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php&#039;.
1CD0 .|    |    |    |    |    |        </cas:authenticationFailure>
1CD0 .|    |    |    |    |    |    </cas:serviceResponse>
1CD0 .|    |    |    |    |    |     [CurlRequest.php:82]
1CD0 .|    |    |    |    |    <= true
1CD0 .|    |    |    |    <= true
1CD0 .|    |    |    |    => CAS_AuthenticationException::__construct(CAS_Client, 'Ticket not validated', 'https://cas2.iut-rodez.fr/cas/serviceValidate?service=https%3A%2F%2Fego.iut-rodez.fr%2Fcaslogger%2Fredirect.php&ticket=ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz', false, false, '<cas:serviceResponse xmlns:cas=\'http://www.yale.edu/tp/cas\'>  <cas:authenticationFailure code=\'INVALID_SERVICE\'>        ticket &#039;ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz&#039; does not match supplied service.  The original service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&amp;_cLs=http://www.iut-rodez.fr/&#039; and the supplied service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php&#039;.  </cas:authenticationFailure></cas:serviceResponse>', 'INVALID_SERVICE', 'ticket \'ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz\' does not match supplied service.  The original service was \'https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&_cLs=http://www.iut-rodez.fr/\' and the supplied service was \'https://ego.iut-rodez.fr/caslogger/redirect.php\'.') [Client.php:2845]
1CD0 .|    |    |    |    |    => CAS_Client::getURL() [AuthenticationException.php:76]
1CD0 .|    |    |    |    |    <= 'https://ego.iut-rodez.fr/caslogger/redirect.php'
1CD0 .|    |    |    |    |    CAS URL: https://cas2.iut-rodez.fr/cas/serviceValidate?service=https%3A%2F%2Fego.iut-rodez.fr%2Fcaslogger%2Fredirect.php&ticket=ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz [AuthenticationException.php:79]
1CD0 .|    |    |    |    |    Authentication failure: Ticket not validated [AuthenticationException.php:80]
1CD0 .|    |    |    |    |    Reason: [INVALID_SERVICE] CAS error: ticket 'ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz' does not match supplied service.  The original service was 'https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&_cLs=http://www.iut-rodez.fr/' and the supplied service was 'https://ego.iut-rodez.fr/caslogger/redirect.php'. [AuthenticationException.php:95]
1CD0 .|    |    |    |    |    CAS response: <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
1CD0 .|    |    |    |    |     <cas:authenticationFailure code='INVALID_SERVICE'>
1CD0 .|    |    |    |    |         ticket &#039;ST-834-9kAtXDWynCHPT4T5xItP-cas2.iut.rdz&#039; does not match supplied service.  The original service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&amp;_cLs=http://www.iut-rodez.fr/&#039; and the supplied service was &#039;https://ego.iut-rodez.fr/caslogger/redirect.php&#039;.
1CD0 .|    |    |    |    |     </cas:authenticationFailure>
1CD0 .|    |    |    |    |    </cas:serviceResponse> [AuthenticationException.php:100]
1CD0 .|    |    |    |    |    exit()
1CD0 .|    |    |    |    |    -
1CD0 .|    |    |    |    -
1CD0 .|    |    |    -
1CD0 .|    |    -
1CD0 .|    -

Same problem with phpCAS1.3.2 and 1.3.4, didn't test with 1.3.3

jfritschi commented 8 years ago

phpcas should be able to deal with reverse proxied setups if the X_Forwarded Headers are properly set. The are evaluated by phpCAS. However some proxies do not properly set protocol and host infos.

I will check if I can spot a bug with the fixed URL code

jbousquie commented 8 years ago

Ok Actually what I meant is that the behavior of getURL() differs in terms of query string management if the fixed URL is set or not.

Ad a side note, my reverse proxy sets correctly the x-forwarded-for header

jfritschi commented 8 years ago

Thanks for the details. With this info I can whip up a patch ;-)

A lot of people struggle because default proxy configs don't set the https protocol info or the loadbalancer use some vendor specific flags. (see othe bug for details: https://github.com/Jasig/phpCAS/issues/178) If all this is properly set up one should not need any fixed urls unless you have a very strange use case. At least this is the goal of the current code ;-)

jbousquie commented 8 years ago

When we set a fixed URL, we can't predict all the incoming parameters with (the query string). That's why the fixed URL is for instance necesseraly like :

https://myapp.domain/path/to/service

and not like

https://myapp.domain/path/to/service?this_fixed_query_string

I'm not expert with the CAS protocol, but imho the fixed URL should be compared the submitted URL without its query string.

Anyway, thank you for your time ;-)

jfritschi commented 8 years ago

I reviewed you debug log again. The problem is that the original ticket was requested with a service URL that was something like "https://ego.iut-rodez.fr/caslogger/redirect.php?_cLt=7HHmTeinEw&_cLs=http://www.iut-rodez.fr/". The comparison that goes wrong is CAS server sided. Your cas client requested and received the ticket with another URL that you are using during verification. The is checked server sided and results from a broken client setup. Please have a look at the debug log from first redirect to authentication and check the service URLs used.

I still have the opinion that a fixed URL is the wrong aproach and will create more issues. Please add some debug trace to the _getURL() and _isHttps(). Just add some phpCAS::trace('Parameter: '.$param); that dump all server variables to the debug log. This will enable you to find out where the url construction goes wrong. I'm pretty sure your proxy does not properly forward host, port or protocol. phpcas itself is very smart about URL generation.

jbousquie commented 8 years ago

Ok thank you I will have a check about it (after next week, because I'll attend a conf)

I still think the problem is because of the fixed URL, because if I set no fixed URL and an easily guessable URL for getURL() with the same ticket and exchanges between the client and the CAS server, then the ticket is validated. I will follow your advices for tracing more accurately and check if the reverse proxy sets really the x-forwarded-proto (I'm sure for ip and port). This will allow to avoid then the usage of the fixed URL.

Thanks again.

Dual-Boot commented 8 years ago

Hi,

I encountered the same kind of problem with a php web application (DokuWiki) and PhpCAS. The problem was in fact à misconfiguration in my proxy virtual host and I could fix it. I post my working configuration here : https://github.com/Jasig/phpCAS/issues/178 If it could help.

Regards,


/ \ /-------------------------------------\ Guy CARRÉ ***** PostMaster - WikiMaster - SysAdmin
"Free Your Mind. Think Open Source"
april.org
_____

----- Mail original ----- De: "Jérôme Bousquié" notifications@github.com À: "Jasig/phpCAS" phpCAS@noreply.github.com Envoyé: Lundi 7 Décembre 2015 20:03:18 Objet: Re: [phpCAS] setFixedServiceURL() prevents getURL() from removing the QS parameters (#181)

Ok thank you I will have a check about it (after next week, because I'll attend a conf)

I still think the problem is because of the fixed URL, because if I set no fixed URL and an easily guessable URL for getURL() with the same ticket and exchanges between the client and the CAS server, then the ticket is validated. I will follow your advices for tracing more accurately and check if the reverse proxy sets really the x-forwarded-proto (I'm sure for ip and port). This will allow to avoid then the usage of the fixed URL.

Thanks again.

— Reply to this email directly or view it on GitHub .

jbousquie commented 8 years ago

Ok thank you. I'll check as soon as I'm back from my conf. I guess the same Apache directive than yours is missing on in my reverse proxy settings.

Thank both of you for your time.

jbousquie commented 8 years ago

Back. I checked your suggestion. This works really well.

Thank you guys :-)

You were right : the fixed URL approach doesn't fit the URL with some variable parameters in the query string.