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.

drh 2025-08-17 18:20 trunk
Commit 06937668056841bb1238a1c651886a193aa5053e1baa64f1073186d64712170c
1 file changed +9 -8
+9 -8
--- src/login.c
+++ src/login.c
@@ -348,12 +348,12 @@
348348
349349
/* Sets a cookie for an anonymous user login, which looks like this:
350350
**
351351
** HASH/TIME/anonymous
352352
**
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.
355355
**
356356
** If zCookieDest is not NULL then the generated cookie is assigned to
357357
** *zCookieDest and the caller must eventually free() it.
358358
**
359359
** If bSessionCookie is true, the cookie will be a session cookie.
@@ -361,20 +361,20 @@
361361
** Search for tag-20250817a to find the code that recognizes this cookie.
362362
*/
363363
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
364364
char *zNow; /* Current time (julian day number) */
365365
char *zCookie; /* The login cookie */
366
- const char *zIpAddr; /* IP Address */
366
+ const char *zUserAgent; /* The user agent */
367367
const char *zCookieName; /* Name of the login cookie */
368368
Blob b; /* Blob used during cookie construction */
369369
int expires = bSessionCookie ? 0 : ANONYMOUS_COOKIE_LIFESPAN;
370370
zCookieName = login_cookie_name();
371371
zNow = db_text("0", "SELECT julianday('now')");
372372
assert( zCookieName && zNow );
373373
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));
376376
sha1sum_blob(&b, &b);
377377
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
378378
blob_reset(&b);
379379
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
380380
if( zCookieDest ){
@@ -1421,25 +1421,26 @@
14211421
if( zUser==0 ){
14221422
/* Invalid cookie */
14231423
}else if( fossil_strcmp(zUser, "anonymous")==0 ){
14241424
/* Cookies of the form "HASH/TIME/anonymous". The TIME must
14251425
** 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
14281428
** "captcha-secret" value in the repository. See tag-20250817a
14291429
** for the code the creates this cookie.
14301430
*/
14311431
double rTime = atof(zArg);
1432
+ const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
14321433
Blob b;
14331434
char *zSecret;
14341435
int n = 0;
14351436
14361437
do{
14371438
blob_zero(&b);
14381439
zSecret = captcha_secret(n++);
14391440
if( zSecret==0 ) break;
1440
- blob_appendf(&b, "%s/%s/%s", zArg, zIpAddr, zSecret);
1441
+ blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
14411442
sha1sum_blob(&b, &b);
14421443
if( fossil_strcmp(zHash, blob_str(&b))==0 ){
14431444
uid = db_int(0,
14441445
"SELECT uid FROM user WHERE login='anonymous'"
14451446
" AND octet_length(cap)>0"
14461447
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button