php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #59612 Problem when using ssl (https) to api
Submitted: 2011-02-08 04:46 UTC Modified: 2011-02-23 11:41 UTC
From: peter at havekes dot eu Assigned: jawed (profile)
Status: Closed Package: oauth (PECL)
PHP Version: 5.3.2 OS: Ubuntu 10.04 64 bit
Private report: No CVE-ID: None
 [2011-02-08 04:46 UTC] peter at havekes dot eu
Description:
------------
I've created a provider using the examples found on
http://djpate.com/2011/01/13/how-to-write-a-complete-oauth-provider-in-php5/

It works fine when I access the api over http://, but when I execute the same request using SSl I get:

oauth_problem=signature_invalid&debug_sbs=GET&http%3A%2F%2Fpublicapi.xxxxxxx.xx%2Foauth%2Fapi%2Fuser&oauth_consumer_key%3D9e01df569515e1d5311a4cb5ae08bd90e354af25%26oauth_nonce%3D123592%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1297157159%26oauth_token%3Dddca6fe1ffe372797ccc39ec944b3cee03c577ac%26oauth_version%3D1.0 Oauth failed

My best quess is something goes wrong when reading the headers in provider.c:

                req_signature = zend_read_property(Z_OBJCE_P(pthis), pthis, OAUTH_PROVIDER_SIGNATURE, sizeof(OAUTH_PROVIDER_SIGNATURE) - 1, 1 TSRMLS_CC);
                if (!signature || !Z_STRLEN_P(req_signature) || strcmp(signature, Z_STRVAL_P(req_signature))) {
                        soo_handle_error(NULL, OAUTH_INVALID_SIGNATURE, "Signatures do not match", NULL, sbs TSRMLS_CC);
                }


Reproduce code:
---------------
try{
  $this->oauth->checkOAuthRequest();
} catch(OAuthException $E){
  echo OAuthProvider::reportProblem($E);
  $this->oauth_error = true;
  die("\nOauth failed");
}

Expected result:
----------------
connections over https should perform the same checks as connections over http


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-02-08 10:34 UTC] jawed@php.net
Yes, that is a problem and it should be taken care of by looking at the 
either the server port or defining a method to force a protocol.

In the mean time, you can do this:

<?php

$old_proto = $_SERVER["HTTP_X_FORWARDED_PROTO"];
$_SERVER["HTTP_X_FORWARDED_PROTO"] = "https";
/// ... oauth checks
$_SERVER["HTTP_X_FORWARDED_PROTO"] = $old_proto;

?>

It's not the correct solution but that's why I'm leaving the bug open and 
we'll address it soon.

- JJ
 [2011-02-08 10:48 UTC] peter at havekes dot eu
Hmmm.. The suggested code gives me :

Notice: Undefined index: HTTP_X_FORWARDED_PROTO in /var/www/oauth/index.php on line 48 oauth_problem=signature_invalid

this is not set in $_SERVER

Array
(
    [REDIRECT_HTTPS] => on
    [REDIRECT_SSL_TLS_SNI] => publicapi.xxxxx.xxx
    [REDIRECT_STATUS] => 200
    [HTTPS] => on
    [SSL_TLS_SNI] => publicapi.xxxxx.xxx
    [HTTP_HOST] => publicapi.xxxxx.xxx
    [HTTP_USER_AGENT] => Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101206 Ubuntu/10.10 (maverick) Firefox/3.6.13
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    [HTTP_ACCEPT_LANGUAGE] => chrome://global/locale/intl.properties
    [HTTP_ACCEPT_ENCODING] => gzip,deflate
    [HTTP_ACCEPT_CHARSET] => ISO-8859-1,utf-8;q=0.7,*;q=0.7
    [HTTP_KEEP_ALIVE] => 115
    [HTTP_CONNECTION] => keep-alive
    [HTTP_REFERER] => http://developer.netflix.com/resources/OAuthTest
    [HTTP_COOKIE] => LOGIN=xxxxxxxx573; SCREEN_NAME=xxxxxxxxxxxxx7144685471456f716363673d3d; __utma=28307180.1055083804.1290586869.1291630341.1296117706.5;
    [HTTP_CACHE_CONTROL] => max-age=0
    [PATH] => /usr/local/bin:/usr/bin:/bin
    [SERVER_SIGNATURE] => <address>Apache/2.2.14 (Ubuntu) Server at publicapi.xxxxxxx.xxx Port 443</address>

    [SERVER_SOFTWARE] => Apache/2.2.14 (Ubuntu)
    [SERVER_NAME] => publicapi.xxxxxx.xxx
    [SERVER_ADDR] => 145.x.y.z
    [SERVER_PORT] => 443
    [REMOTE_ADDR] => 145.a.b.c
    [DOCUMENT_ROOT] => /var/www/
    [SERVER_ADMIN] => webmaster@localhost
    [SCRIPT_FILENAME] => /var/www/oauth/index.php
    [REMOTE_PORT] => 41309
    [REDIRECT_QUERY_STRING] => oauth_consumer_key=9e01df569515e1d5311a4cb5ae08bd90e354af25&oauth_nonce=1236&oauth_signature=gtZoEafN95g%2BwORC3w1DBssKHi8%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1297179867&oauth_token=ddca6fe1ffe372797ccc39ec944b3cee03c577ac&oauth_version=1.0
    [REDIRECT_URL] => /oauth/api/user
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => oauth_consumer_key=9e01df569515e1d5311a4cb5ae08bd90e354af25&oauth_nonce=1236&oauth_signature=gtZoEafN95g%2BwORC3w1DBssKHi8%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1297179867&oauth_token=ddca6fe1ffe372797ccc39ec944b3cee03c577ac&oauth_version=1.0
    [REQUEST_URI] => /oauth/api/user?oauth_consumer_key=9e01df569515e1d5311a4cb5ae08bd90e354af25&oauth_nonce=1236&oauth_signature=gtZoEafN95g%2BwORC3w1DBssKHi8%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1297179867&oauth_token=ddca6fe1ffe372797ccc39ec944b3cee03c577ac&oauth_version=1.0
    [SCRIPT_NAME] => /oauth/index.php
    [PHP_SELF] => /oauth/index.php
    [REQUEST_TIME] => 1297179953
)
 [2011-02-08 11:22 UTC] rasmus@php.net
I run over SSL from servers behind an nginx load balancer.  
In the load balancer nginx config I simply have:

proxy_set_header X-Forwarded-Proto https;

In provider.c we check this header:

zend_hash_find(Z_ARRVAL_P(PG(http_globals)
[TRACK_VARS_SERVER]), "HTTP_X_FORWARDED_PROTO", 
sizeof("HTTP_X_FORWARDED_PROTO"), (void **)&proto);

and do:

spprintf(&tmp, 0, "%s://%s%s", Z_STRVAL_PP(proto), 
Z_STRVAL_PP(host), Z_STRVAL_PP(uri));

We also check:

zend_hash_find(Z_ARRVAL_P(PG(http_globals)
[TRACK_VARS_SERVER]), "SERVER_PORT", sizeof("SERVER_PORT"), 
(void**)&port);

and do:

spprintf(&tmp, 0, "http%s://%s%s", 
Z_LVAL_PP(port)==443?"s":"", Z_STRVAL_PP(host), 
Z_STRVAL_PP(uri));

So, if you are running SSL on port 443, you can set the Port 
server variable, which hopefully should be set by your web 
server already, or if you are doing fancy proxying where the 
port at the OAuth server may not match the incoming port, 
you will have to proxy the protocol header correctly from 
your initial point of entry.
 [2011-02-09 09:12 UTC] peter at havekes dot eu
I am running ssl on port 443, and [SERVER_PORT] => 443 is set. No fancy proxy'ing..

Why is it not working?
 [2011-02-23 03:47 UTC] bas dot zoetekouw at surfnet dot nl
(Ok, turns out that I can in fact add comments directly.  
Please excuse me for also bugging you by email).

I'm experiencing the same problem here.  The issue seems to 
be the way
in which the check for the port is made in
oauth_provider_get_current_uri().

If I change

  spprintf(&tmp, 0, "http%s://%s%s", 
Z_LVAL_PP(port)==443?"s":"",
Z_STRVAL_PP(host), Z_STRVAL_PP(uri));

to

  spprintf(&tmp, 0, "http%s://%s%s",
strcmp(Z_STRVAL_PP(port),"443")?"":"s", Z_STRVAL_PP(host),
Z_STRVAL_PP(uri));

on line 427, everything works as expected. 

I'm not very familiar with Zend, and these Z_LVAL_PP and 
Z_STRVAL_PP
macros are very underdocumented, it seems, so I'm
not sure if the Z_LVAL_PP thing is supposed to work.  Is it 
supposed to
automatically run strtol() or so?
In any case, some debugging (with fprintf()s) shows that the 
integer
value of Z_LVAL_PP(port) is -1198437340 rather than
443.

Anyway, should be easy to fix.  Hope this is useful.
 [2011-02-23 05:08 UTC] jawed@php.net
That's odd, can you tell us what $_SERVER["SERVER_PORT"] reports for 
you?

- JJ
 [2011-02-23 06:47 UTC] bas dot zoetekouw at surfnet dot nl
$_SERVER["SERVER_PORT"] is 443 (or 80, if I access the page 
over plain HTTP).

To check things out, I added some debugging code to the 
oauth_provider_get_current_uri() function that prints out 
the content of the post 
variable:

    FILE *debug = fopen("/tmp/oauth.txt","w");
    fprintf(debug,"port.val.lval=%li\n", (**port).value.lval 
);
    fprintf(debug,"port.val.dval=%f\n", (**port).value.dval 
);
    fprintf(debug,"port.val.str.len=%i\n", 
(**port).value.str.len );
    fprintf(debug,"port.val.str.val=%s\n", 
(**port).value.str.val );
    fprintf(debug,"port.type=%i\n", (**port).type );
    fclose(debug);

This results in:

  port.val.lval=-1208471000
  port.val.dval=0.000000
  port.val.str.len=3
  port.val.str.val=443
  port.type=6

So it indeed seems that the string value "443" is not 
automatically converted to a long.

Indeed, looking at zend.h, it seems a type of 6 indicates 
that the variable is a string, so it kind of makes sense 
that the other value 
types are undefined.  Pretty sloppy programming though in 
Zend: the Z_LVAL_PP macro really should emit a warning if 
the type of the variable 
is not an integer.

In any case, the solution of using strcmp() instead of a 
integer-based comparison seems to be correct.
 [2011-02-23 08:05 UTC] datibbaw@php.net
added SEPARATE_ZVAL() and convert_to_long_ex() to coerce the
port into a long value before comparing it to 443.

This makes it more future proof, in case anyone changes the
type into long ;-)

my code change was untested though, so please check!
 [2011-02-23 08:56 UTC] bas dot zoetekouw at surfnet dot nl
Latest trunk (r308598) works perfectly for me, both over http 
and https.

Thanks!
 [2011-02-23 11:41 UTC] jawed@php.net
This bug has been fixed in SVN.

In case this was a documentation problem, the fix will show up at the
end of next Sunday (CET) on pecl.php.net.

In case this was a pecl.php.net website problem, the change will show
up on the website in short time.
 
Thank you for the report, and for helping us make PECL better.

Thx Tjerk.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 30 14:01:28 2024 UTC