698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
|
zRef = P("HTTP_REFERER");
if( zRef==0 ) return 0;
nBase = (int)strlen(g.zBaseURL);
if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
return 1;
}
/*
** Return true if the current request appears to be safe from a
** Cross-Site Request Forgery (CSRF) attack. Conditions that must
** be met:
**
** * The HTTP_REFERER must have the same origin
** * The REQUEST_METHOD must be POST - or requirePost==0
*/
int cgi_csrf_safe(int requirePost){
if( requirePost ){
const char *zMethod = P("REQUEST_METHOD");
if( zMethod==0 ) return 0;
if( strcmp(zMethod,"POST")!=0 ) return 0;
}
return cgi_same_origin();
}
/*
** If bLoginVerifyCsrf is true, this calls login_verify_csrf() to
** verify that the secret injected by login_insert_csrf_secret() is in
** the CGI environment and valid. If that fails, it does so
** fatally. If that passes and cgi_csrf_safe(1) returns false, this
** fails fatally with a message about a cross-site scripting attempt,
** else it returns without side effects.
*/
void cgi_csrf_verify(int bLoginVerifyCsrf){
if( bLoginVerifyCsrf!=0 ){
login_verify_csrf_secret();
}
if( 0==cgi_csrf_safe(1) ){
fossil_fatal("Cross-site request forgery attempt");
}
}
/*
** Information about all query parameters, post parameter, cookies and
** CGI environment variables are stored in a hash table as follows:
*/
|
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
>
>
>
>
|
|
>
|
<
<
|
|
>
>
>
>
>
>
>
>
|
>
>
>
|
|
|
|
<
<
<
|
<
<
<
|
|
|
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
|
zRef = P("HTTP_REFERER");
if( zRef==0 ) return 0;
nBase = (int)strlen(g.zBaseURL);
if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
return 1;
}
/*
** Return true if the current CGI request is a POST request
*/
static int cgi_is_post_request(void){
const char *zMethod = P("REQUEST_METHOD");
if( zMethod==0 ) return 0;
if( strcmp(zMethod,"POST")!=0 ) return 0;
return 1;
}
/*
** Return true if the current request appears to be safe from a
** Cross-Site Request Forgery (CSRF) attack. The level of checking
** is determined by the parameter. The higher the number, the more
** secure we are:
**
** 0: Request must come from the same origin
** 1: Same origin and must be a POST request
** 2: All of the above plus must have a valid CSRF token
**
** Results are cached in the g.okCsrf variable. The g.okCsrf value
** has meaning as follows:
**
** -1: Not a secure request
** 0: Status unknown
** 1: Request comes from the same origin
** 2: (1) plus it is a POST request
** 3: (2) plus there is a valid "csrf" token in the request
*/
int cgi_csrf_safe(int securityLevel){
if( g.okCsrf<0 ) return 0;
if( g.okCsrf==0 ){
if( !cgi_same_origin() ){
g.okCsrf = -1;
}else{
g.okCsrf = 1;
if( cgi_is_post_request() ){
g.okCsrf = 2;
if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
g.okCsrf = 3;
}
}
}
}
return g.okCsrf >= (securityLevel+1);
}
/*
** Verify that CSRF defenses are maximal - that the request comes from
** the same origin, that it is a POST request, and that there is a valid
** "csrf" token. If this is not the case, fail immediately.
*/
void cgi_csrf_verify(void){
if( !cgi_csrf_safe(2) ){
fossil_fatal("Cross-site Request Forgery detected");
}
}
/*
** Information about all query parameters, post parameter, cookies and
** CGI environment variables are stored in a hash table as follows:
*/
|