Fossil SCM
Use the UserAgent value from the HTTP request header, rather than the client IP address, as the additional factor in the anonymous login cookie hash, since some client are on networks where their IP address can shift frequently.
Commit
06937668056841bb1238a1c651886a193aa5053e1baa64f1073186d64712170c
Parent
68da4784aa3d5f3…
1 file changed
+9
-8
+9
-8
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -348,12 +348,12 @@ | ||
| 348 | 348 | |
| 349 | 349 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 350 | 350 | ** |
| 351 | 351 | ** HASH/TIME/anonymous |
| 352 | 352 | ** |
| 353 | -** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which SECRET | |
| 354 | -** is captcha-secret. | |
| 353 | +** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET | |
| 354 | +** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value. | |
| 355 | 355 | ** |
| 356 | 356 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 357 | 357 | ** *zCookieDest and the caller must eventually free() it. |
| 358 | 358 | ** |
| 359 | 359 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| @@ -361,20 +361,20 @@ | ||
| 361 | 361 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 362 | 362 | */ |
| 363 | 363 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 364 | 364 | char *zNow; /* Current time (julian day number) */ |
| 365 | 365 | char *zCookie; /* The login cookie */ |
| 366 | - const char *zIpAddr; /* IP Address */ | |
| 366 | + const char *zUserAgent; /* The user agent */ | |
| 367 | 367 | const char *zCookieName; /* Name of the login cookie */ |
| 368 | 368 | Blob b; /* Blob used during cookie construction */ |
| 369 | 369 | int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN; |
| 370 | 370 | zCookieName = login_cookie_name(); |
| 371 | 371 | zNow = db_text("0", "SELECT julianday('now')"); |
| 372 | 372 | assert( zCookieName && zNow ); |
| 373 | 373 | blob_init(&b, zNow, -1); |
| 374 | - zIpAddr = PD("REMOTE_ADDR","nil"); | |
| 375 | - blob_appendf(&b, "/%s/%z", zIpAddr, captcha_secret(0)); | |
| 374 | + zUserAgent = PD("HTTP_USER_AGENT","nil"); | |
| 375 | + blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0)); | |
| 376 | 376 | sha1sum_blob(&b, &b); |
| 377 | 377 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 378 | 378 | blob_reset(&b); |
| 379 | 379 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 380 | 380 | if( zCookieDest ){ |
| @@ -1421,25 +1421,26 @@ | ||
| 1421 | 1421 | if( zUser==0 ){ |
| 1422 | 1422 | /* Invalid cookie */ |
| 1423 | 1423 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1424 | 1424 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must |
| 1425 | 1425 | ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and |
| 1426 | - ** the sha1 hash of TIME/IPADDR/SECRET must match HASH. IPADDR | |
| 1427 | - ** is the remote address of the client and SECRET is the | |
| 1426 | + ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT | |
| 1427 | + ** is the HTTP_USER_AGENT of the client and SECRET is the | |
| 1428 | 1428 | ** "captcha-secret" value in the repository. See tag-20250817a |
| 1429 | 1429 | ** for the code the creates this cookie. |
| 1430 | 1430 | */ |
| 1431 | 1431 | double rTime = atof(zArg); |
| 1432 | + const char *zUserAgent = PD("HTTP_USER_AGENT","nil"); | |
| 1432 | 1433 | Blob b; |
| 1433 | 1434 | char *zSecret; |
| 1434 | 1435 | int n = 0; |
| 1435 | 1436 | |
| 1436 | 1437 | do{ |
| 1437 | 1438 | blob_zero(&b); |
| 1438 | 1439 | zSecret = captcha_secret(n++); |
| 1439 | 1440 | if( zSecret==0 ) break; |
| 1440 | - blob_appendf(&b, "%s/%s/%s", zArg, zIpAddr, zSecret); | |
| 1441 | + blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret); | |
| 1441 | 1442 | sha1sum_blob(&b, &b); |
| 1442 | 1443 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1443 | 1444 | uid = db_int(0, |
| 1444 | 1445 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1445 | 1446 | " AND octet_length(cap)>0" |
| 1446 | 1447 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -348,12 +348,12 @@ | |
| 348 | |
| 349 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 350 | ** |
| 351 | ** HASH/TIME/anonymous |
| 352 | ** |
| 353 | ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which SECRET |
| 354 | ** is captcha-secret. |
| 355 | ** |
| 356 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 357 | ** *zCookieDest and the caller must eventually free() it. |
| 358 | ** |
| 359 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| @@ -361,20 +361,20 @@ | |
| 361 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 362 | */ |
| 363 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 364 | char *zNow; /* Current time (julian day number) */ |
| 365 | char *zCookie; /* The login cookie */ |
| 366 | const char *zIpAddr; /* IP Address */ |
| 367 | const char *zCookieName; /* Name of the login cookie */ |
| 368 | Blob b; /* Blob used during cookie construction */ |
| 369 | int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN; |
| 370 | zCookieName = login_cookie_name(); |
| 371 | zNow = db_text("0", "SELECT julianday('now')"); |
| 372 | assert( zCookieName && zNow ); |
| 373 | blob_init(&b, zNow, -1); |
| 374 | zIpAddr = PD("REMOTE_ADDR","nil"); |
| 375 | blob_appendf(&b, "/%s/%z", zIpAddr, captcha_secret(0)); |
| 376 | sha1sum_blob(&b, &b); |
| 377 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 378 | blob_reset(&b); |
| 379 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 380 | if( zCookieDest ){ |
| @@ -1421,25 +1421,26 @@ | |
| 1421 | if( zUser==0 ){ |
| 1422 | /* Invalid cookie */ |
| 1423 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1424 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must |
| 1425 | ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and |
| 1426 | ** the sha1 hash of TIME/IPADDR/SECRET must match HASH. IPADDR |
| 1427 | ** is the remote address of the client and SECRET is the |
| 1428 | ** "captcha-secret" value in the repository. See tag-20250817a |
| 1429 | ** for the code the creates this cookie. |
| 1430 | */ |
| 1431 | double rTime = atof(zArg); |
| 1432 | Blob b; |
| 1433 | char *zSecret; |
| 1434 | int n = 0; |
| 1435 | |
| 1436 | do{ |
| 1437 | blob_zero(&b); |
| 1438 | zSecret = captcha_secret(n++); |
| 1439 | if( zSecret==0 ) break; |
| 1440 | blob_appendf(&b, "%s/%s/%s", zArg, zIpAddr, zSecret); |
| 1441 | sha1sum_blob(&b, &b); |
| 1442 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1443 | uid = db_int(0, |
| 1444 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1445 | " AND octet_length(cap)>0" |
| 1446 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -348,12 +348,12 @@ | |
| 348 | |
| 349 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 350 | ** |
| 351 | ** HASH/TIME/anonymous |
| 352 | ** |
| 353 | ** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET |
| 354 | ** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value. |
| 355 | ** |
| 356 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 357 | ** *zCookieDest and the caller must eventually free() it. |
| 358 | ** |
| 359 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| @@ -361,20 +361,20 @@ | |
| 361 | ** Search for tag-20250817a to find the code that recognizes this cookie. |
| 362 | */ |
| 363 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 364 | char *zNow; /* Current time (julian day number) */ |
| 365 | char *zCookie; /* The login cookie */ |
| 366 | const char *zUserAgent; /* The user agent */ |
| 367 | const char *zCookieName; /* Name of the login cookie */ |
| 368 | Blob b; /* Blob used during cookie construction */ |
| 369 | int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN; |
| 370 | zCookieName = login_cookie_name(); |
| 371 | zNow = db_text("0", "SELECT julianday('now')"); |
| 372 | assert( zCookieName && zNow ); |
| 373 | blob_init(&b, zNow, -1); |
| 374 | zUserAgent = PD("HTTP_USER_AGENT","nil"); |
| 375 | blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0)); |
| 376 | sha1sum_blob(&b, &b); |
| 377 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 378 | blob_reset(&b); |
| 379 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 380 | if( zCookieDest ){ |
| @@ -1421,25 +1421,26 @@ | |
| 1421 | if( zUser==0 ){ |
| 1422 | /* Invalid cookie */ |
| 1423 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ |
| 1424 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must |
| 1425 | ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and |
| 1426 | ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT |
| 1427 | ** is the HTTP_USER_AGENT of the client and SECRET is the |
| 1428 | ** "captcha-secret" value in the repository. See tag-20250817a |
| 1429 | ** for the code the creates this cookie. |
| 1430 | */ |
| 1431 | double rTime = atof(zArg); |
| 1432 | const char *zUserAgent = PD("HTTP_USER_AGENT","nil"); |
| 1433 | Blob b; |
| 1434 | char *zSecret; |
| 1435 | int n = 0; |
| 1436 | |
| 1437 | do{ |
| 1438 | blob_zero(&b); |
| 1439 | zSecret = captcha_secret(n++); |
| 1440 | if( zSecret==0 ) break; |
| 1441 | blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret); |
| 1442 | sha1sum_blob(&b, &b); |
| 1443 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1444 | uid = db_int(0, |
| 1445 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1446 | " AND octet_length(cap)>0" |
| 1447 |