Is htmlspecialchars sufficient against low level XSS in my case? - xss

I have added functionality to my admin so it preserves the URL which you tried to access before it asked you to login. So, if you go to:
/admin/foo/bar?baz
It'll redirect you to:
/admin/auth/login
After you login, before my function add-on you always went to /admin/user/profile. Right now, I save /admin/foo/bar?baz in a session variable, $_SESSION['from'].
In the login <form>, the hidden value takes the value of the session:
<input type="hidden" name="from" value="<?php echo htmlspecialchars($_SESSION['from'];)?>">
Then, after the form is submitted a redirect takes place:
header('Location: ' . $_POST['from'] );
I have seen other questions relating to XSS and htmlspecialchars and am aware it won't fix all possible XSS attempts, but would this work successfully against "low level" XSS attempts?

While there's no XSS attack here, if you're using a slightly older version of PHP, you'll open yourself up to HTTP header injection, which can be worse in some cases.
If you're fetching the URL-to-be-returned-to from the HTTP referrer, then you should be protected well enough by making sure the URL is one that you control by parsing it, then only storing the return path and query string. When performing the final redirect, you should make sure that the components of the path are properly URL encoded. You can store the return URL entirely in the session instead of punting it back out to the user to possibly manipulate during the login.

Related

what could be the secure alternative way of writing response.url in the redirect method in JS?

Security team reported following code as a Cross-site scripting.
 
 redirect: function redirect(ajax, response, status) {
  window.location = response.url;
 });
They gave the sample of writing in better way
$(document).ready(function(){
$("#myDiv").on("click", "button", function(){
var eid = $("#eid").val();
$("resultsDiv").append(eid);
});
});
But How I do it for my code?
It is potential cross-site scripting on the response.url parameter, because if that contains user input and the user enters javascript: alert(1), it will be run in some browsers. Whether this is exploitable depends on the circumstances (how and with what parameters is redirect called and what browser is used).
The other code really doesn't make any sense though. :)
As for the fix, it also depends on the above. Make sure callers don't include user input, and/or in redirect() you can implement input validation (eg. response.url starts with 'http', does not contain 'javascript:', etc.)
Also note that if not cross-site scripting, it can stil be an open redirect.

CFWheels: Redirect to URL with Params Hidden

I am using redirectTo() function with params to redirect to another pages with a query string in the url. For security purpose this does not look appealing because the user can change the parameters in the url, thus altering what is inserted into the database.
My code is:
redirectTo(action="checklist", params="r=#r#&i=#insp#&d=#d#");
Is there anyway around this? I am not using a forms, I just wish to redirect and I want the destination action/Controller to know what I am passing but not display it in the url.
You can obfuscate the variables in the URL. CfWheels makes this really easy.
All you have to do is call set(obfuscateURLs=true) in the config/settings.cfm file to turn on URL obfuscation.
I am sure this works with linkTo() function. I hope it works with RedirectTo() funcation as well. I do not have a set up to check it now. But if doesn't work for RedirectTo(), you can obfuscateParam() and deObfuscateParam() functions to do job for you.
Caution: This will only make harder for user to guess the value. It doesn't encrypt value.
To know more about this, Please read the document configuration and defaults and obfuscating url
A much better approach to this particular situation is to write params to the [flash].1 The flash is exactly the same thing as it is in Ruby on Rails or the ViewBag in ASP.Net. It stores the data in a session or cookie variable and is deleted at the end of the next page's load. This prevents you from posting back long query strings like someone that has been coding for less than a year. ObfuscateParam only works with numbers and is incredibly insecure. Any power user can easily deobfuscate, even more so with someone that actually makes a living stealing data.

Django forms URLField allow empty scheme

I have a Django form that uses a 'forms.URLField' like local_url1 = URLField(label="First Local URL", required=False). If a user inputs something like 'https://www.google.com' then the field validates without error.
However, if the user puts 'www.google.com' the field fails validation and the user sees an error. This is because the layout of a URL is scheme://host:port/absolute_path and the failing URL is missing the scheme (e.g. https), which Django's URLFieldValidation expects.
I don't care if my users include the scheme and nor should my form. Unfortunately, the error from django is completely useless in indicating what is wrong, and I've had multiple users ask why it says to enter a valid URL. I'm also certain I've lost paying customers because of this.
Is there a way to have all the other validation of a URL take place, but ignore the fact that the scheme is missing? At the very least, can I change the error message to add something like "Did you include http?". I've attempted implementing my own URLField and URLFieldValidation, but unless that's the path I have to take, then that is a different StackOverflow question.
I'm using Django 1.7, by the way. Thanks for any help!
URL/URI scheme list to validate against. If not provided, the default
list is ['http', 'https', 'ftp', 'ftps']. As a reference, the IANA Web
site provides a full list of valid URI schemes.
If the valid URI schemes provided by IANA web are not what you are looking for, then I suggest you create your own field validator.
Remember that URLField is a subclass of the CharField. and since www.something.com is ok with you, then It's simple to add a regular expression to the regular CharField that checks if the pattern is correct or not.
A regular expression like this for example will validate against www and http://. so with or without http or https.
((?:https?\:\/\/|www\.)(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)
www.google.com -- OK
http://www.google.com -- OK
https://www.google.com -- OK
http://google.com -- OK
https://google.com -- OK
However, this will not complain about blahwww.domain.com
so you might enhance it as you like.

How to deal with IE's not encoding URL in django?

In browsers like chrome and firefox, if you type something like this into the address line:
http://mysite.com/q?name=喇叭嘴
they help you encode the url into:
http://mysite.com/?q=%E5%96%87%E5%8F%AD%E5%98%B4
So, in django, you can just use:
request.GET.get('q', '')
to get the value '喇叭嘴'.
However, this thing does not happen in IE(msie, thanks). That's why we keep on getting ��� instead.
With ajax handling, I can just use JavaScript encodeURI() Function to work around with it. However, triggering it every time is such a pain.
Is there any way to handle the issue in the backend?

Cookie to log in with Jsoup?

For a project I'm trying to get data from a website only acessible when you're logged in from the site Goodreads.com. I'm new to Jsoup, since I'm using it only for this particular project. Getting the relevant data from the website is not a problem, but I can't seem to get to the particular page I need. The page I'm trying to acces is viewable only when logged in, when not logged in it rederects to the log-in page.
I've looked through the answers here, but the answers given so far have not helped.
What I have now:
String url = "http://www.goodreads.com/friend/user/7493379-judith";
Connection.Response res = Jsoup.connect("http://www.goodreads.com/user/sign_in")
.data("email", "MYEMAIL", "user_password", "MYPASSWORD")
.method(Connection.Method.POST)
.execute();
Document doc2 = res.parse();
String sessionId = res.cookie("_session_id");
Document doc = Jsoup.connect(url)
.cookie("_session_id", sessionId)
.get();
I got this far with help of the answers here, but it doesn't work, I'm still only getting the data from the log-in page it rederects to.
I have several questions:
Most importantly of course; How can I make it work?
The given answers here heve used method.(Method.POST) instead of method.(Connection.Method.POST) . When I use the first one however, I get an error that Method cannot be resolved. Anyone know why this is?
The examples I've seen have used "username" and "password" in .data() . What exactly do these refer to? I've now used the name of the input box. Is it the name, the type, the id, what exactly? Since Goodreads does not refer to the log in as the username, but as the e-mail, I assume I have to change them. (username & password doesn't work either)
Examples also use http://example.com/login.php as example url. Goodreads doesn't have a /login.php page though. Am I correct to assume I have to use the url with the log-in screen?
_session_id is the name of the relevant cookie on Goodreads.
I'd be very grateful if anyone can point me in the right direction!
See carefully what data is posted on login:
user[email]:email#email
remember_me:on
user[password]:plain_pasword
n:667387
So your post must execute exact same keys.
2.Make sure, you make right import: import org.jsoup.Connection.Method;
but Connection.Method.POST is still good.
3.See p1
4.Yes, you are correct
5.what is the question?
Goodreads requires two things when logging in: first, that you have a session ID stored in a cookie, and second, that you have a random generated number. You can get these when first visiting the login page without logging in: it will set a cookie with a session ID, and the form will contain a hidden input form (i.e. ) with the name "n" and value a number. Save these and pass them along as respectively a cookie and a form value when logging in.
Some remarks about the way I found this out:
The first thing you need to realise is that you're trying to recreate the exact same requests your browser does with Jsoup. So, in order to check whether what you have right now will work, you can try to recreate the exact same situation with your browser.
To recreate your code, I went to the login page, then I deleted all my Goodreads cookies (as you don't send along any cookies when you send the login request as well), and attempted to sign in with only passing the username and password form values. It gave an error that my session had timd out. When I first loaded the login page and then deleted all cookies except the session ID and did not remove the "n" form value, I could log in successfully. Therefore, you want to make a general GET request to the sign in page first, retrieve the session ID cookie you get there and the hidden form value, and pass it along with the POST request.
It could be that the API changed or that there just are several ways. Using Connection.Method.POST will do fine, in any case.
Yes, they refer to the names of the input boxes. This should be id, however, since name was used in the past and not all versions of all browsers supported passing the ids as data, most websites are just adding both. Either should be fine.
If you look at the source code of the sign in form, you can see that the "method" attribute of the form element is indeed the sign in page itself, so that's where it sends the request to.
PS. As a general tip, you can use the Firefox extension "Tamper Data" to remove form data or even cookies (though there are easier extensions for that).
You can log in with this code:
public static void main(String[] args) throws Exception {
Connection.Response execute = Jsoup
.connect("https://www.goodreads.com/")
.method(Connection.Method.GET).execute();
Element sign_in = execute.parse().getElementById("sign_in");
String authenticityToken = sign_in.select("input[name=authenticity_token]").first().val();
String n = sign_in.select("input[name=n]").first().val();
Document document = Jsoup.connect("https://www.goodreads.com/user/sign_in")
.data("cookieexists", "✓")
.data("authenticity_token", authenticityToken)
.data("user[email]", "user#email.com")
.data("user[password]", "password")
.data("remember_me", "on")
.data("n", n)
.cookies(execute.cookies())
.post();
}