Skip to content

C#: HttpOnly and Secure cookie queries #5579

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 2 additions & 23 deletions csharp/ql/src/Security Features/CWE-614/RequireSSL.ql
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,8 @@ import csharp
import semmle.code.asp.WebConfig
import semmle.code.csharp.frameworks.system.Web

class FormsElement extends XMLElement {
FormsElement() {
this = any(SystemWebXMLElement sw).getAChild("authentication").getAChild("forms")
}

string getRequireSSL() { result = getAttribute("requireSSL").getValue().trim().toLowerCase() }

predicate isRequireSSL() { getRequireSSL() = "true" }
}

class HttpCookiesElement extends XMLElement {
HttpCookiesElement() { this = any(SystemWebXMLElement sw).getAChild("httpCookies") }

string getRequireSSL() { result = getAttribute("requireSSL").getValue().trim().toLowerCase() }

predicate isRequireSSL() {
getRequireSSL() = "true"
or
not getRequireSSL() = "false" and
exists(FormsElement forms | forms.getFile() = getFile() | forms.isRequireSSL())
}
}

// the query is a subset of `cs/web/cookie-secure-not-set` and
// should be removed once it is promoted from experimental
from XMLElement element
where
element instanceof FormsElement and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>
Cookies without <code>HttpOnly</code> flag are accessible to JavaScript running in the same origin. In case of
Cross-Site Scripting (XSS) vulnerability the cookie can be stolen by malicious script.
</p>
</overview>

<recommendation>
<p>
Protect sensitive cookies, such as related to authentication, by setting <code>HttpOnly</code> to <code>true</code> to make
them not accessible to JavaScript. In ASP.NET case it is also possible to set the attribute via <code>&lt;httpCookies&gt;</code> element
of <code>web.config</code> with the attribute <code>httpOnlyCookies="true"</code>.
</p>
</recommendation>

<example>

<p>
In the example below <code>Microsoft.AspNetCore.Http.CookieOptions.HttpOnly</code> is set to <code>true</code>.
</p>

<sample src="httponlyflagcore.cs" />

<p>
In the following example <code>CookiePolicyOptions</code> are set programmatically to configure defaults.
</p>

<sample src="cookiepolicyoptions.cs" />

<p>
In the example below <code>System.Web.HttpCookie.HttpOnly</code> is set to <code>true</code>.
</p>

<sample src="httponlyflag.cs" />

</example>

<references>

<li><a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookieoptions.httponly">CookieOptions.HttpOnly Property,</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie</a> Header,</li>
<li><a href="https://msdn.microsoft.com/en-us/library/system.web.httpcookie.httponly(v=vs.110).aspx">HttpCookie.HttpOnly Property,</a></li>
<li><a href="https://msdn.microsoft.com/library/ms228262%28v=vs.100%29.aspx">httpCookies Element,</a></li>

</references>
</qhelp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* @name 'HttpOnly' attribute is not set to true
* @description Omitting the 'HttpOnly' attribute for security sensitive data allows
* malicious JavaScript to steal it in case of XSS vulnerability. Always set
* 'HttpOnly' to 'true' to authentication related cookie to make it
* not accessible by JavaScript.
* @kind problem
* @problem.severity warning
* @precision high
* @id cs/web/cookie-httponly-not-set
* @tags security
* external/cwe/cwe-1004
*/

import csharp
import semmle.code.asp.WebConfig
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.microsoft.AspNetCore
import experimental.dataflow.flowsources.AuthCookie

from Expr httpOnlySink
where
exists(Assignment a, Expr val |
httpOnlySink = a.getRValue() and
val.getValue() = "false" and
(
exists(ObjectCreation oc |
getAValueForProp(oc, a, "HttpOnly") = val and
(
oc.getType() instanceof SystemWebHttpCookie and
isCookieWithSensitiveName(oc.getArgument(0))
or
exists(MethodCall mc, MicrosoftAspNetCoreHttpResponseCookies iResponse |
oc.getType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
iResponse.getAppendMethod() = mc.getTarget() and
isCookieWithSensitiveName(mc.getArgument(0)) and
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
not exists(
OnAppendCookieHttpOnlyTrackingConfig config, DataFlow::Node source,
DataFlow::Node sink
|
config.hasFlow(source, sink)
) and
// Passed as third argument to `IResponseCookies.Append`
exists(
CookieOptionsTrackingConfiguration cookieTracking, DataFlow::Node creation,
DataFlow::Node append
|
cookieTracking.hasFlow(creation, append) and
creation.asExpr() = oc and
append.asExpr() = mc.getArgument(2)
)
)
)
)
or
exists(PropertyWrite pw |
(
pw.getProperty().getDeclaringType() instanceof MicrosoftAspNetCoreHttpCookieBuilder or
pw.getProperty().getDeclaringType() instanceof
MicrosoftAspNetCoreAuthenticationCookiesCookieAuthenticationOptions
) and
pw.getProperty().getName() = "HttpOnly" and
a.getLValue() = pw and
DataFlow::localExprFlow(val, a.getRValue())
)
)
)
or
exists(Call c |
httpOnlySink = c and
(
exists(MicrosoftAspNetCoreHttpResponseCookies iResponse, MethodCall mc |
// default is not configured or is not set to `Always`
not getAValueForCookiePolicyProp("HttpOnly").getValue() = "1" and
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
not exists(
OnAppendCookieHttpOnlyTrackingConfig config, DataFlow::Node source, DataFlow::Node sink
|
config.hasFlow(source, sink)
) and
iResponse.getAppendMethod() = mc.getTarget() and
isCookieWithSensitiveName(mc.getArgument(0)) and
(
// `HttpOnly` property in `CookieOptions` passed to IResponseCookies.Append(...) wasn't set
exists(ObjectCreation oc |
oc = c and
oc.getType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
not isPropertySet(oc, "HttpOnly") and
exists(
CookieOptionsTrackingConfiguration cookieTracking, DataFlow::Node creation,
DataFlow::Node append
|
cookieTracking.hasFlow(creation, append) and
creation.asExpr() = oc
)
)
or
// IResponseCookies.Append(String, String) was called, `HttpOnly` is set to `false` by default
mc = c and
mc.getNumberOfArguments() < 3
)
)
or
exists(ObjectCreation oc |
oc = c and
oc.getType() instanceof SystemWebHttpCookie and
isCookieWithSensitiveName(oc.getArgument(0)) and
// the property wasn't explicitly set, so a default value from config is used
not isPropertySet(oc, "HttpOnly") and
// the default in config is not set to `true`
not exists(XMLElement element |
element instanceof HttpCookiesElement and
element.(HttpCookiesElement).isHttpOnlyCookies()
)
)
)
)
select httpOnlySink, "Cookie attribute 'HttpOnly' is not set to true."
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
public class Startup
{
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCookiePolicy(new CookiePolicyOptions()
{
Secure = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always,
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class MyController : Controller
{
void Login()
{
var cookie = new System.Web.HttpCookie("cookieName") { HttpOnly = true };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class MyController : Controller
{
void Login()
{
var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions() { HttpOnly = true };
Response.Cookies.Append("auth", "secret", cookieOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>
Sensitive data that is transmitted using HTTP is vulnerable to being read by a third party. By default,
cookies are sent via HTTP, not HTTPS.
</p>
</overview>

<recommendation>
<p>
In ASP.NET case when using cookies ensure that HTTPS is used by setting the property <code>Microsoft.AspNetCore.Http.CookieOptions.Secure</code> to <code>true</code>.
</p>
<p>
In ASP.NET Core case when using cookies, ensure that HTTPS is used, either via the <code>&lt;forms&gt;</code> attribute above, or
the <code>&lt;httpCookies&gt;</code> element, with the attribute <code>requireSSL="true"</code>. It is also possible to require cookies
to use HTTPS programmatically, by setting the property <code>System.Web.HttpCookie.Secure</code> to <code>true</code>.
</p>
</recommendation>

<example>

<p>
In the example below <code>Microsoft.AspNetCore.Http.CookieOptions.Secure</code> is set to <code>true</code> programmatically.
</p>

<sample src="secureflagcore.cs" />

<p>
In the following example <code>CookiePolicyOptions</code> are set programmatically to configure defaults.
</p>

<sample src="cookiepolicyoptions.cs" />

<p>
In the example below <code>System.Web.HttpCookie.Secure</code> is set to <code>true</code> programmatically.
</p>

<sample src="secureflag.cs" />

</example>

<references>

<li><a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookieoptions.secure">CookieOptions.Secure Property,</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie</a> Header,</li>
<li><a href="https://msdn.microsoft.com/en-us/library/system.web.security.formsauthentication.requiressl(v=vs.110).aspx">FormsAuthentication.RequireSSL Property,</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/1d3t3c61(v=vs.100).aspx">forms Element for authentication,</a></li>
<li><a href="https://msdn.microsoft.com/library/ms228262%28v=vs.100%29.aspx">httpCookies Element,</a></li>

</references>
</qhelp>
Loading