Secure Password Sent from Native App to HTTPS API - javascript

I have a HTTPS API server, and want to connect to it from a native Android app (built in Cordova). The app uses is a simple XHR request like so:
function authenticate() {
const username = document.querySelector('#usernameInput').value;
const password = document.querySelector('#passwordInput').value;
const xhr = new XMLHttpRequest();
const body = JSON.stringify({
username,
password,
});
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
xhr.onload = function () {
console.log(this.responseText);
};
xhr.onerror = function () {
alert('Request failed');
};
xhr.send(body);
}
function handleSubmit(event) {
event.preventDefault();
authenticate();
}
document.querySelector('#submitButton').addEventListener('click', handleSubmit);
The request works fine and the username/password are authenticated on the server or 403 is returned. My concern is I can see the password in plain text if I look at the request payload in a local debugger. Does this mean passwords are exposed over the network? If so, what should I be doing differently?

When using HTTPS only your computer and the server know the key to decrypt the data in the request, since they previously agreed on a key during the key exchange phase of the SSL/TLS connection.
Other computers in your network should not be able to read your request's content since they don't have the key. You can see it in plain text in your browser's debugger because it is plain text before you actually send it.
Don't worry about manually encrypting your data before sending it to the server, the protocol already takes care of that.

Related

JS - Making a Login page using an XMLHttpRequest

I am attempting to create a login page on my mini site that allows users to download material. I am struggling with the xhr request, in order to check if the password and username are valid. Is the following code sufficient in what I am trying to do? Any help would be appreciated.
function login() {
const xhr = new XMLHttpRequest();
const uri = "localhost"; // uri for request inserted here
xhr.open("GET", uri, true, Username, Password);
xhr.withCredentials = true;
xhr.onload= function() {
const resp = JSON.parse(xhr.responseText);
document.getElementById("Login").innerHTML = resp;
}
xhr.send(null)
}
Although it is your personal wish but one should never check username and password validity using xhr. These should always be done with form submissions. And the next thing is that it is never safe to use GET in requests for fields such as passwords. Always use passwords.
You need to edit your code.

Sending email from browser mailgun

I want to send a simple email from the browser using mailgun. I send a working email from postman and with the download code function I managed to get the javascript code which is:
var data = new FormData();
data.append("from", "Mailgun Sandbox <postmaster#sandbox1985406854ad9e8b8dfe094531fa41e8.mailgun.org>");
data.append("to", "Example <example#gmail.com>");
data.append("subject", "Hello from Mailgun");
data.append("text", "Congratulations Example, you just sent an email with Mailgun! You are truly awesome!");
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://api:key-75a05cfds8c66bd1y4c3e854305438e5#api.mailgun.net/v3/sandbox1985406854ad9e8b8dfe094531fa41e8.mailgun.org/messages");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.setRequestHeader("postman-token", "0a3ad9d5-22b5-6308-d6e7-59f66360fa26");
xhr.send(data);
But when I execute this code from my browser (firefox) it does nothing. What am I doing wrong?
Changing the url from
https://api:key-75a05cfds8c66bd1y4c3e854305438e5#api.mailgun.net/v3/sandbox1985406854ad9e8b8dfe094531fa41e8.mailgun.org/messages
to
https://api.mailgun.net/v3/sandbox1985406854ad9e8b8dfe094531fa41e8.mailgun.org/messages
seems to execute, is the api:key-xx part invalid?
Don't do this. This is not an appropriate use of the Mailgun API.
By delivering this Javascript to a browser, you are giving them access to your Mailgun API key. This API key is not limited in any way whatsoever -- a malicious client could misuse the key to send an unlimited number of messages to any recipient they want, potentially racking up large bills and/or getting your account blocked for spam.
Use the Mailgun API on the server side. If you need to send messages via Javascript, create an endpoint in your application which calls the Mailgun API with appropriate restrictions in place.

How to make http authentication in REST API call from javascript

I need to call OpenMRS REST API from Java script to get data from OpenMRS. Below is my java script code:
function myfunction(){
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:8081/openmrs-standalone/ws/rest/v1/person?q=John", false);
xhr.setRequestHeader("Authorization: Basic YWRtaW46QWRtaW4xMjM");
xhr.send("");
alert(xhr.status);
}
Where YWRtaW46QWRtaW4xMjM is my base64 coded username:password as explained here. If I do not put the authorization line in the code and check the web app using Firebug, it returns 401 unauthorized status that is expected. But if I put the authorization, nothing is returned and in firebug I do not see any response as well. If I check the URL directly on browser, the page asks for username and password and after giving correct credential, it returns the data normaly. So I am getting some problem of providing the http authentication right from the java script of the app. I have also considered the methods explained here but no luck. Can anyone please help me to authorize the http request right from the javascript?
Here is another similar but different example of how to set the header for authorization purposes, but instead using JQuery and AJAX.
var token = "xyz"
var url = "http://localhost:8081/openmrs-standalone/ws/rest/v1/person?q=John"
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + token)
},
})
.done(function (data) {
$.each(data, function (key, value) {
// Do Something
})
})
.fail(function (jqXHR, textStatus) {
alert("Error: " + textStatus);
})
Below is also an example of how you might get an access token using xhr instead of AJAX.
var data = "grant_type=password&username=myusername#website.com&password=MyPassword";
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://somewebsite.net/token");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.setRequestHeader("client_id", "4444-4444-44de-4444");
xhr.send(data);
Beware of cross-site domain requests(if you're requesting a token that's not on localhost or within the domain that you are currently working in), as you'll need CORS for that. If you do run into a cross-domain issue, see this tutorial for help, and be sure you have enabled CORS requests from the API as well.

Access HTTPS wcf service in HTML,Javascript, service and Web page hosted on different domains - CORS

I am trying to access WCF service hosted on server. The service is using SSL connection.
I assigned a self signed certificate in IIS.
The service is a restfull service.
I tried with two different scenarios:
1st scenario :
I hosted the web site to same server(client application which access service)
In this case the service is accessible and able to get response from service.
2nd scenario :
I hosted the web site to localhost,local machine.
From here when I am trying to make request to service method which is hosted on different web server it is not accessible.
Showing error "Method not allowed"
Here is code :
function testHttps()
{
jsonText3=JSON.stringify({"Name":"Avinash"});
url="https://ServerIP/WcfSecureService/Service.svc/GetName";
var xhr = createCORSRequest('POST', url);
if (!xhr) {
alert('CORS not supported');
}
xhr.onload = function() {
alert(xhr.responseText);
};
xhr.onerror = function() {
alert('Woops, there was an error making the request.');
};
xhr.setRequestHeader('Content-Type', 'application/json') ;
xhr.send(jsonText3);
}
function createCORSRequest(method, url)
{
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr)
{
xhr.open(method, url, true);
}
else if (typeof XDomainRequest != "undefined")
{
xhr = new XDomainRequest();
xhr.open(method, url);
} else
{
xhr = null;
}
return xhr;
}
How to make CORS call success with HTTPS?
I do not want to install any certificate on client site.
How to authenticate service from Javascript?
Please suggest...
Thanks for your help in advance.
--Avinash
Check this and this out.
I think your problem is not about https: is the server responding with an header like the following?
Access-Control-Allow-Origin: *
In case it's not, it's probably filtering out your local referer.
Furthermore, if you're using credentials the server can't use the "*" wildcard and has to specify every allowed domains, which don't include localhost, I guess.

iOS: Authentication using XMLHttpRequest - Handling 401 response

I'm writing an iOS application using PhoneGap (aka Cordova), I have a simple html login page that logs the user in using an XMLHttpRequest with basic authentication over SSL. Everything works splendidly when you enter your username and password correctly. However, if you enter the wrong username/password none of my callbacks are ever called.
If you run the same code on Chrome for example, with the wrong username/password, chrome behaves in a similar manner, except it pops up an authentication challenge dialog. Hitting cancel on chrome's dialog returns control to my javascript code. Unfortunately, on iOS, the UIWebView wont even popup an auth dialog, it just hangs. I need a way to tell the user that they entered the wrong username or password so they can retry.
The closest thing to an answer I could find was this http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax but changing the response status from the server doesn't seem like the right thing to do.
Here's basically what my request code looks like, but when a bad username or password is sent it never reaches my onload callback (in fact the onreadystatechange callback only gets called once and thats for readyState 1, aka OPEN).
var req = new XMLHttpRequest();
req.onload = function(ev) {
if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else if (req.status == 200) {
window.location.href = some_secure_site;
} else {
// edit //
alert("Some other status");
}
}
req.onerror = function (ev) { alert('Error'); };
req.ontimeout = function(ev) { alert('Timeout'); };
req.open('GET', uri, true, userValue, passValue);
req.withCredentials = true;
req.send();
A few things became apparent to me while trying to do this on iOS. One is that iOS has a bug relating to basic auth, so if your password has certain special characters in it you'll never get a response back from your server because your server will never get an authentication challenge. That is, if you're using the username and password field in the "open" method.
My guess is they are doing something stupid like sending it via http://username:password#myorigin.com/etc when they should be using http headers and base64 encoding the creds like so
req.setRequestHeader("Authorization", "Basic " + base64(username) + ':' + base64(password));
The other thing I learned is that Basic Auth isnt very secure and is prone to a million and one problems. One of which that will annoy you is that the client will cache the username and password, which will override any new values you send via "req.open(...)". Good luck getting around that using javascript alone, you'll have to do some magic in ObjC to clear the cache.
If you have control over your server, I would suggest using token authentication. Connect over SSL and then send a POST with JSON data containing the username and password. The server could then send back JSON data with an authentication token (essentially a bunch of random characters long enough that it can't ever be guessed, a UUID works well. this is generated by the server and can only be known to the client and the server). Then store the token and the username in the keychain so the user doesnt need to enter their creds everytime they start your app.
My server will always send back a 200 response but the JSON data will contain the information needed to either retry or to store the auth token. In general... basic auth basically sucks.
try {
var req = new XMLHttpRequest();
req.onload = function(ev) {
var response = JSON.parse(this.responseText);
if (response.success === true) {
// The server will respond with a token that will allow us to login
storeCredentials(userValue, response.token);
// redirect with token
else if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else {
alert("Some other status");
}
}
req.ontimeout = setTimeout(function(ev) { navigator.notification.alert('Timeout trying to contact the server'); }, 10000);
req.onerror = function(ev) { clearTimeout(this.ontimeout); navigator.notification.alert('Error connecting to the server during authentication.'); };
var uri = myWebOrigin + '/authenticate';
req.open('POST', uri, true);
req.setRequestHeader('Cache-Control', 'no-cache');
req.setRequestHeader('Content-Type', 'application/json');
json_data = {username : Base64.encode(userValue), password : Base64.encode(passValue)};
req.send(JSON.stringify(json_data));
} catch(error) {
navigator.notification.alert('Uh oh, an error occurred trying to login! ' + error);
return;
}
I just had the same issues with none of the callbacks being called when using iOS + PhoneGap + jQuery. If I pass incorrect credentials and use
$.ajax({
...
timeout: 5000, // Some timeout value that makes sense
...
});
then the error callback is called with {"readyState":0,"status":0,"statusText":"timeout"}. In that case you would have to guess that the real error is the HTTP 401.
Alternatively you can use
$.ajax({
...
async: false, // :-(
...
});
and your error callback will get something like {"readyState":4,"responseText":"<html>...</html>","status":401,"statusText":"Unauthorized"} back.
To solved this problem remove the header WWW-Authenticate from server response.
There is probably an other HTTP status code beside the 401 and 200 codes received! Make sure there is really no other status code received:
if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else if (req.status == 200) {
window.location.href = some_secure_site;
} else {
alert('Unfetched status code '+req.status+' captured!');
}
I've been having the same issue - none of the callbacks on the request are being called if invalid credentials are passed in.
My guess is that internally the UIWebView is being told to prompt for credentials, but that prompt is being suppressed, leading to this bug. This is based on all the other browsers I've tried (Safari, Chrome, Firefox) not calling the callbacks in this case until after the prompt is dismissed.
Another solution (the one I'm using) would be to not use Javascript to do the authentication, and instead do it on the iOS side - you can use the code on How to display the Authentication Challenge in UIWebView? as a rough template for doing this.

Categories

Resources