Content-security-policy blocking Twitter Login (abraham/twitteroauth) - twitter-oauth

I'm setting up login with social media for my app, but my content-security-policy is blocking twitter login from working as intended.
I use window.open to create the popup and post to the twitter library php file. This part works fine. I can click the authorize app button and the user is logged in. The problem is the popup fails to close and the user is not redirected to the members page.
This is the csp log I receive every time the error occurs.
{
"csp-report": {
"document-uri": "https://dev.example.com/twitter-callback.php?oauth_token=Fr5kdwAAAAAAy_TdAAABaTlfL8o&oauth_verifier=i418eqFom1jKd3jYrpirNvAlPJnOBedG",
"referrer": "https://api.twitter.com/oauth/authorize",
"violated-directive": "script-src-elem",
"effective-directive": "script-src-elem",
"original-policy": "upgrade-insecure-requests; default-src https:; connect-src 'self'; font-src 'self' data:; frame-src accounts.google.com platform.twitter.com syndication.twitter.com staticxx.facebook.com www.facebook.com www.google.com; frame-ancestors 'none'; img-src 'self' data: platform.twitter.com syndication.twitter.com *.twimg.com; script-src 'self' 'unsafe-inline' platform.twitter.com/widgets.js apis.google.com/ cdn.polyfill.io/v2/polyfill.min.js cdn.syndication.twimg.com/timeline/profile cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js connect.facebook.net/en_GB/sdk.js connect.facebook.net/en_US/sdk.js platform.twitter.com www.google.com/recaptcha/api.js www.gstatic.com/charts/ www.gstatic.com/recaptcha/ 'nonce-cjJas4W2X3GtCJszEQ0UZtZqie1hGOWr'; style-src 'self' 'unsafe-inline' blob: cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/ www.gstatic.com/charts/ platform.twitter.com *.twimg.com; base-uri 'none'; object-src 'self'; manifest-src 'self'; report-uri /csp-report/csp.php;",
"disposition": "enforce",
"blocked-uri": "inline",
"line-number": 1,
"source-file": "https://dev.example.com/twitter-callback.php?oauth_token=Fr5pdwAAAy_TAABaTlfL8o&oauth_verifier=i418eqFm1jrNvAlPJnOBedG",
"status-code": 0,
"script-sample": ""
}
} at Date: March 01 2019 13:08:56
If I turn off the csp the login works perfectly OK so it's definitely the csp causing the issue.
Edit: I've tried to add a script-src-elem policy to the CSP e.g script-src-elem 'self' 'unsafe-inline' api.twitter.com/oauth/authorize, but it didn't work.

I figured this out myself in the end.
The problem was caused by having a nonce in script-src. My php script injects inline javascript to close the popup window and redirect, but I forgot about adding a nonce to those script tags.
// close child window and redirect parent window then exit
echo "<script nonce=\"$nonce\">
if (window.opener) {
window.opener.location.href = 'members.php';
window.close();
} else {
window.location.href = 'members.php';
}
</script>";
exit;
Done

Related

CSP problem execute inline script Paypal button

Im using the braintree javascript v3 sdk and using a paypal checkout button for my shop.
Code example:
braintree.client.create({
authorization: 'sandbox_xxxx'
}, function(err, clientInstance) {
if (err) {
console.log(err);
return;
}
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
if (paypalCheckoutErr) {
console.error('Error creating PayPal Checkout:', paypalCheckoutErr);
return;
}
paypal.Button.render({
env: 'sandbox',
commit: true,
buttonStyle: {
color: 'blue',
shape: 'rect',
size: 'medium'
},
payment: function () {
return paypalCheckoutInstance.createPayment({
flow: 'checkout',
amount: '10.00',
currency: 'EUR'
});
},
onAuthorize: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
document.getElementById("paynonce").value = payload.nonce;
document.getElementById("paymentform").submit();
});
},
onCancel: function (data) {
console.log('checkout.js payment cancelled', JSON.stringify(data, 0, 2));
},
onError: function (err) {
console.error('checkout.js error', err);
}
}, '#paypal-button').then(function () {
});
});
});
To secure my application im using my Content security police:
add_header Content-Security-Policy "default-src 'none';
img-src 'self' *.paypal.com data:;
manifest-src 'self';
style-src 'self' 'unsafe-inline' *.braintreegateway.com *.braintree-api.com https://www.gstatic.com https://fonts.googleapis.com;
script-src 'self' 'nonce-xxxx' *.paypal.com *.paypalobjects.com *.braintreegateway.com https://www.gstatic.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' *.paypal.com *.paypalobjects.com *.braintreegateway.com *.braintree-api.com https://fonts.googleapis.com https://www.google-analytics.com https://www.gstatic.com https://fonts.gstatic.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-src *.paypal.com *.braintreegateway.com *.braintree-api.com;
frame-ancestors 'none';";
The button is working fine but the problem is i still recieve reports and errors because the paypal executes inline Javascript:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-xxxx' *.paypal.com *.paypalobjects.com *.braintreegateway.com https://www.gstatic.com". Either the 'unsafe-inline' keyword, a hash ('sha256-xxx='), or a nonce ('nonce-...') is required to enable inline execution.
[Report Only] Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-xxxx' *.paypal.com *.paypalobjects.com *.braintreegateway.com https://www.gstatic.com".
As you can see i whitelisted all important urls. I do also add a nonce to run the scripts:
<script nonce="xxxx" src="https://www.paypalobjects.com/api/checkout.js" data-version-4 log-level="warn"></script>
<script nonce="xxxx" src="https://js.braintreegateway.com/web/3.55.0/js/paypal-checkout.min.js"></script>
Not sure it has to do with:
For cross site cookies i use session.cookie_samesite = Strict
Get this warnings:
A cookie associated with a cross-site resource at http://developer.paypal.com/ was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
A cookie associated with a cross-site resource at http://www.paypal.com/ was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
For a total of 9 paypal subdomains.
EDIT:
I checked my html and found there are multiple inline scripts rendered to the paypalbutton html check my attachment.
How i can solve this problem ?
For the cookie warnings, these are associated with PayPal's domains and it is their responsibility to update them. In current stable Chrome, these warnings are purely informational and are not affecting behaviour. However, if you're using Canary, Dev, or Beta versions you may experience those cookies being blocked.
More context is available at:
https://web.dev/samesite-cookie-recipes
https://www.chromium.org/updates/same-site
It sounds as if those PayPal scripts are trying to inject additional scripts in the page. You may want to consider 'strict-dynamic' to allow the trust to propagate to additional resources:
script-src 'nonce-xxxx' 'strict-dynamic';
This will cause the whitelist or source expressions such as 'self' or 'unsafe-inline', but you can also include them for browsers that do not support strict-dynamic.
Your errors are specifically about 'unsafe-inline' and 'unsafe-eval', so for older browsers you may need to consider those as well. However, I'd test with the strict-dynamic first to see if that meets your needs.
script-src 'nonce-xxxx' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' 'self' *.paypal.com *.paypalobjects.com *.braintreegateway.com https://www.gstatic.com;
I would also validate that you definitely do not have any inline scripts in the page that you have missed (either from your own code or other third-party services that are not PayPal), in case these are the source of the errors.

Content security policy blocks my rest calls in Vue.js

I just started working with vue.js and try to send a post request to my server, but the request is blocked by CSP.
Error Message:
Refused to connect to 'http://127.0.0.1:5000/login' because it violates the following Content Security Policy directive: "connect-src 'self' ws:".
I have already tried to change my meta-tag but have not come to any solution.
<meta http-equiv=Content-Security-Policy content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:; connect-src 'self' ws:;">
Rest call:
export default {
name: "Login",
data() {
return {
loading: false,
login: {
email: "",
password: ""
}
}
},
methods: {
auth(){
fetch("http://127.0.0.1:5000/login",{
body: JSON.stringify(this.login),
method: "POST",
headers:{
"Content-Type": "application/json"
},
credentials: 'same-origin'
})
.then(res =>{
severdata = JSON.parse(res)
console.log(serverdata)
})
console.log(this.login.email)
this.loading = true;
setTimeout(() => {
this.loading = false;
}, 5000);
}
}
};
</script>```
In your server , you need to return the Content-Security-Policy header.
To allow everything (unsafe), use the following (found on this post):
default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';

What is fetch's redirect and authorization headers expected behavior? (Safari has different behavior from other Mac browsers)

In short, when using an authorization header with the fetch api and a redirect is followed Chrome, FireFox, and Opera (on a Mac) include the authorization header in the second request. However, Safari (12.0.1) does not. From the fetch api spec and issue #553 my understanding is that the header should be included. Is this a correct understanding of how fetch is supposed to work?
Here's the simplified code I'm using with a workaround for Safari but I'd like to know if there's something I'm doing wrong that's causing the behavior with Safari or if there is a better workaround.
export async function loadData(token) {
const opts = {headers: {Authorization: `Bearer ${token}`}, credentials: 'include', redirect: 'follow'};
let response = await fetch('/api/data', opts);
// Work around for Safari not including headers in redirected request
if (response.status === 401 && response.redirected) {
response = await fetch(response.url, opts);
}
if (response.ok) {
return response.json();
}
return null;
}
Quick edit some additional info about the redirect. The redirected location is to the same origin and is a 302:
content-length: 118
content-type: text/plain; charset=utf-8
date: Mon, 26 Nov 2018 20:12:18 GMT
location: /api/data/current-version
server: nginx
status: 302
strict-transport-security: max-age=15768000
vary: Accept

Http request fail on NativeScript Angular

I have the Groceries tutorial, working. Then I go to the user service and change the URL of the login:
login(user: User) {
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this.http.post(
"http://ws.u-vox.com/api/noauth/loginvenue",
JSON.stringify({
username: user.email,
password: user.password,
}),
{ headers: headers }
)
.map(response => response.json())
.do(data => {
Config.token = data.Result.access_token;
})
.catch(this.handleErrors);
}
handleErrors(error: Response) {
console.log(JSON.stringify(error.json()));
return Observable.throw(error);
}
When I press sign in I would expect a http message and a JSON on the terminal.
But instead I'm getting this mess, and I don't know what is happening.
CONSOLE LOG file:///app/shared/user/user.service.js:38:20: {"line":993,"column":38,"sourceURL":"file:///app/tns_modules/nativescript-angular/zone-js/dist/zone-nativescript.js","originalStack":"ZoneAwareError#file:///app/tns_modules/nativescript-angular/zone-js/dist/zone-nativescript.js:993:38\nfile:///app/tns_modules/tns-core-modules/http/http-request/http-request.js:86:37\nUIApplicationMain#[native code]\nstart#file:///app/tns_modules/tns-core-modules/application/application.js:211:26\nbootstrapApp#file:///app/tns_modules/nativescript-angular/platform-common.js:72:28\nbootstrapModule#file:///app/tns_modules/nativescript-angular/platform-common.js:60:26\nanonymous#file:///app/main.js:7:57\nevaluate#[native code]\nmoduleEvaluation#[native code]\n[native code]\npromiseReactionJob#[native code]","zoneAwareStack":"file:///app/tns_modules/tns-core-modules/http/http-request/http-request.js:86:37 [<root>]\nUIApplicationMain#[native code] [<root>]\nstart#file:///app/tns_modules/tns-core-modules/application/applicati
CONSOLE ERROR file:///app/tns_modules/nativescript-angular/zone-js/dist/zone-nativescript.js:569:26: Unhandled Promise rejection: Animation cancelled. ; Zone: <root> ; Task: null ; Value: Error: Animation cancelled. _rejectAnimationFinishedPromise#file:///app/tns_modules/tns-core-modules/ui/animation/animation-common.js:98:31 [<root>]
My endpoint works outside NativeScript app:
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Content-Type: application/json
Transfer-Encoding: chunked
Connection: close
Cache-Control: no-cache, private
Date: Thu, 28 Dec 2017 15:00:39 GMT
Allow: POST
{"username ... }
I think this issue is due to CORS (cross domain) request.
First make sure the url on which you make the post request have its CORS enabled.
Second, if you are using iOS to run your app, add the following to /nativescript/App_Resources/iOS/info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<!--Include to allow all connections (DANGER)-->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
For android I think there is an equivalent setting.

How to override Facebook CSP in Firefox Add-on

Facebook must have changed something in their Content Security Policy header, because my Firefox Add-on suddenly stopped working.
I need to get a remote XML file, using XMLHttpRequest, and my domain of course isn't included in Facebook CSP (in Firefox console, I see an error with the connect-src policy of the page). It was working like a charm, until today.
It is worth nothing that Firefox Add-ons should not be affected by CSP of the server (this in theory).
So, I need to override Facebook's CSP in order to get my Add-on working again. I found this code, but it's for Chrome Extensions.
chrome.webRequest.onHeadersReceived.addListener(function (details)
{
for (i = 0; i < details.responseHeaders.length; i++) {
if (details.responseHeaders[i].name.toUpperCase() == "X-WEBKIT-CSP") {
details.responseHeaders[i].value = "default-src *;script-src https://*.feedhound.co https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl 'unsafe-inline' 'unsafe-eval' https://*.akamaihd.net http://*.akamaihd.net;style-src * 'unsafe-inline';connect-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.spotilocal.com:* https://*.akamaihd.net ws://*.facebook.com:* http://*.akamaihd.net https://*.feedhound.co";
}
}
return {
responseHeaders : details.responseHeaders
};
}, {
urls : ["*://*.facebook.com/*"],
types : ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]
},
["blocking", "responseHeaders"]
);
Someone can help me implementing something similar for a Firefox Add-on?
I already tried an alternative method, adding my own "content-src" security header with setRequestHeader but without any luck.
Thanks,
Roberto
Thank you, in the meantime I tried a similar solution, but I found that unfortunately it won't be allowed by Mozilla (my fix has been rejected by the reviewer).
The problem is that modifying CSP headers, by setting a new server response with setResponseHeader, may lead to security issues.
I guess that the only acceptable solution would be to run the code inside the chrome, instead of the content, so it wouldn't be affected by server's CSP.
This was my fix:
var MYADDON_CSP_listener = {
observe : function(aSubject, aTopic, aData) {
if (aTopic == "http-on-examine-response") {
let url;
aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
url = aSubject.URI.spec;
if (/https?:\/\/www.facebook.com\//.test(url)) {
var csp = aSubject.getResponseHeader("content-security-policy");
csp = csp.replace('connect-src', 'connect-src http://*.mywebsite.com https://*.mywebsite.com');
aSubject.setResponseHeader("content-security-policy", csp, false);
}
}
}
};
var MYADDON_observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
MYADDON_observerService.addObserver(MYADDON_CSP_listener, "http-on-examine-response", false);
I have the same issue and was able to resolve it. Look at the solution i have posted here
https://stackoverflow.com/a/19917664/297113
That may help you.