In my new project, I am going to include google recaptcha.
my question is fairly simple even if we do client side validation that user is not a robot even though it is suggested to do server side validation.
I want to know why it is necessary to do server side validation for google recaptcha? how does it add the extra layer of security? and how to do in spring boot with spring security?
Server side validation is MUST !! reCAPTCHA is designed in a way that client side just generates the 'g-captcha-response' which along with secret key (stored at server-side) is sent to https://www.google.com/recaptcha/api/siteverify for validation. The response is a JSON which states sucesss true or false and it is further pushed to client side. Validating only at the client side is technically possible, but it defeats the purpose. Moreover, you may get CORS (Cross-Origin Resource Sharing) policy error in console if you do only client side validation. I can share steps to do simple java based server side validation in servlet. Let me know if you need that.
Here is the code. Few points to be noted:
The parameter userResponse = request.getParameter("recaptchaResponse") is the way by which i am getting the 'g-recaptcha-response' generated by the user when he clicked reCAPTCHA widget on UI. On your javascript, capture the value of field 'g-recaptcha-response' and pass it appended to request. Then in servlet, we can get it from request.getParameter.
Sample code:
var recaptchaResponse = document.getElementById("g-recaptcha-response").value;
//alert("g-recaptcha-response= "+recaptchaResponse);
if (recaptchaResponse.length > 0)
{
var xmlhttp1;
if (window.XMLHttpRequest)
{
xmlhttp1=new XMLHttpRequest();
}
else
{
xmlhttp1=new ActiveXObject("Microsoft.XMLHTTP");
}
var query1 = "?recaptchaResponse=" + recaptchaResponse;
xmlhttp1.open("POST","captchaVerificationServlet" + query1, false);
xmlhttp1.send(null);
var resp1 = xmlhttp1.responseText;
alert("resp1= "+resp1);
if(resp1=='matched'){
return true;
}
else{
alert("resp1 did not match");
return false;
}
}
else{
alert("error: recaptcha response is blank");
return false;
}
For simplicity i am checking presence of "success:true" in returned JSON response. As you know, returned JSON contains two parameters : success and error-codes. You may use a JSONReader to read and parse JSON and obtain all parameters fully. Sample code will be like
JsonReader rdr = Json.createReader(your_inputstream);
JsonObject jsonObject = rdr.readObject();
Needless to say, remove all alerts and sop statements in production!
public class CaptchaVerificationServlet extends HttpServlet {
private static final String sec = YOUR_SECRET_KEY;
public void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userResponse = request.getParameter("recaptchaResponse");
response.setCharacterEncoding("UTF-8");
System.out.println("userResponse= "+userResponse);
//verify user response with Google ReCaptcha API
String ipAddress = request.getRemoteAddr(); //get client's ip address
System.out.println("ipAddress= "+ipAddress);
try{
String s = validateCaptcha(sec, userResponse, ipAddress);
Boolean success = (s.contains("\"success\": true"));
if(success)
response.getWriter().write("matched");
}
catch(Exception ioe){
ioe.printStackTrace();
ioe.printStackTrace(response.getWriter());
}
}
private String validateCaptcha(String secret, String response, String remoteip) throws IOException
{
URLConnection connection = null;
InputStream is = null;
String output = "";
String proxyHost = "YOUR_PROXY_NAME";
int proxyPort = 80; //proxy server port, generally 80 or 443 (confirm from sys-admin)
SocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
Proxy httpProxy = new Proxy(Proxy.Type.HTTP, addr);
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
String password = "changeit";
System.setProperty("javax.net.ssl.trustStore",filename);
System.setProperty("javax.net.ssl.trustAnchors",filename);
System.setProperty("javax.net.ssl.trustStorePassword",password);
String charset = Charset.forName("UTF-8").name();
String url = "https://www.google.com/recaptcha/api/siteverify";
try {
String query = String.format("secret=%s&response=%s&remoteip=%s",
URLEncoder.encode(secret, charset),
URLEncoder.encode(response, charset),
URLEncoder.encode(remoteip, charset));
URL fullURL = new URL(url + "?" + query);
connection = fullURL.openConnection(httpProxy);
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0");
is = connection.getInputStream();
System.out.println("connection InputStream");
BufferedReader reader = null;
String responseXXX = "";
reader = new BufferedReader(new InputStreamReader(is));
responseXXX = reader.readLine();
while (responseXXX!=null) {
output+= responseXXX;
responseXXX = reader.readLine();
}
System.out.println("Output: " + output);
}
finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
//cannot do anything here
}
}
}
return output;
}
}
Related
I am using the HttpClient but my results are taking up to 6 seconds coming back from the same machine on the same subnet and ip range of 192.168. When I call the api directly from the ip address the results are more or less instant so why is it so slow with httpclient on the same computer.
I have seen other so's that suggest set to use proxy as false is the best way to go.
I have also tested this on a stock phone and it takes around 8 seconds for the login to be successful on the phone.
private HttpClient _client;
public async Task<String> Getusers()
{
var content = "";
HttpClientHandler hch = new HttpClientHandler();
hch.Proxy = null;
hch.UseProxy = false;
_client = new HttpClient(hch);
var uri = new Uri(Constants.ApiEndPoint + "/Users"); // Your url is here
try
{
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
throw ex;
}
return content;
}
Here is my login method in case anyone can see something wrong with it.
private async void BtnLogin_Clicked(object sender, EventArgs e)
{
string content = await Getusers(); //Sends a GET request to the specified Uri and returns the response body as a string in an asynchronous operation
List<Users> _users = JsonConvert.DeserializeObject<List<Users>>(content); //Deserializes or converts JSON String into a collection of Post
var userName = txtUserName.Text;
var password = txtPassword.Text;
var isValidUser = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault();
var driverId = _users.Where(w => w.UserName == userName && w.password == password).FirstOrDefault().ID;
if (isValidUser != null)
{
Application.Current.Properties["driverId"] = driverId;
Application.Current.MainPage = new MainPage();
}
else
{
lblError.Text = "Error your credentials are invalid, please try again";
}
}
I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps
I'm developing Xamarin Cross platform application. I'm trying to connect to server (http://test.net/login/clientlogin), I need to send these fields (password = "xyz"; platform = iphone; (useremail) = "test#test.com";) along with the request. So that server will check these parameters and returns XML. But we don't know how to add these fields to the request.
When i open the above string url (http://*****/login/clientlogin) i am getting login screen, with in that we have username, password and platform text fields.
Thanks in advance!!!..
This should get you started presuming you are adding the values as headers in the request:
public class TestClient
{
HttpClient client;
public TestClient(){
this.client = new HttpClient ();
}
public void AddHeadersAndGet(){
client.DefaultRequestHeaders.Add ("username", "whatevertheusernameis");
this.GetAsync<WhateverObjectTypeYouAreReceiving> ("theurloftheservice");
}
public async Task<T> GetAsync<T>(string address){
HttpResponseMessage response = null;
response = await client.GetAsync (address);
if (response.IsSuccessStatusCode) {
try {
var responseString = await response.Content.ReadAsStringAsync ();
return new T (Serializer.DeserializeObject<T> (responseString),
response.StatusCode);
} catch (Exception ex) {
}
} else {
}
}
}
The key line for you is:
client.DefaultRequestHeaders.Add ("username", "whatevertheusernameis");
I'm attempting to build a GET webservice that would from website 1 initiate a GET request...sending that request to website 2 and website two would respond by sending a list of objects. I using Json.net to serialize and deserialize the List of objects.
I've put together a POST webservice with the assistance of this question.. WebService ASP.NET MVC 3 Send and Receive
But I've been unsuccessful so far at adapting that example for my new requirement.
Here is what I have so far from website 1..
public static List<ScientificFocusArea> ScientificFocusAreas()
{
string apiURL = "http://localhost:50328/Api/GetAPI";
//Make the post
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
//var bytes = Encoding.Default.GetBytes(body);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiURL);
Stream stream = null;
try
{
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
request.Timeout = -1;
request.Method = "GET";
}
finally
{
if (stream != null)
{
stream.Flush();
stream.Close();
}
}
List<ScientificFocusArea> listSFA = WebService.GetResponse_ScientificFocusArea(request);
return listSFA;
}
public static List<ScientificFocusArea> GetResponse_ScientificFocusArea(HttpWebRequest request)
{
List<ScientificFocusArea> listSFA = new List<ScientificFocusArea>();
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
{
throw new HttpException((int)response.StatusCode, response.StatusDescription);
}
var end = string.Empty;
using (StreamReader reader = new StreamReader(responseStream))
{
end = reader.ReadToEnd();
reader.Close();
listSFA = JsonConvert.DeserializeObject<List<ScientificFocusArea>>(end);
}
response.Close();
}
}
return listSFA;
}
Then on the website 2...
public class GetAPIController : Controller
{
//
// GET: /Api/GetAPI/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetScientificFocusAreas()
{
//Get list of SFAs
List<ScientificFocusArea> ListSFA = CreateList.ScientificFocusArea();
string json = JsonConvert.SerializeObject(ListSFA, Formatting.Indented);
//Send the the seralized object.
return Json(json);
}
}
Also, on website 2, I've registered this route for the incoming request...
context.MapRoute(
"GetScientificFocusAreas",
"Api/GetAPI/",
new
{
controller = "GetAPI",
action = "GetScientificFocusAreas",
id = UrlParameter.Optional
}
);
I'm currently getting the error.. he remote server returned an error: (404) Not Found.
Any help would me greatly appreciated.
The problem seems like a routing issue. I would start with the RouteDebugger which can be found here. This tool gives insight into which routes your URL is hitting.
The code I use for a HTTP GET is a bit different that what you have above. It's included below.
public T Get<T>(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
using (Stream responseStream = response.GetResponseStream())
{
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
{
throw new HttpException((int)response.StatusCode, response.StatusDescription);
}
var end = string.Empty;
using (StreamReader reader = new StreamReader(responseStream))
{
end = reader.ReadToEnd();
reader.Close();
}
responseStream.Close();
response.Close();
JsonSerializer serializer = new JsonSerializer();
serializer.Binder = new DefaultSerializationBinder();
JsonReader jsonReader = new JsonTextReader(new StringReader(end));
T deserialize = serializer.Deserialize<T>(jsonReader);
return deserialize;
}
}
catch (Exception ex)
{
throw new ApiException(string.Format("An error occured while trying to contact the API. URL: {0}", url), ex);
}
}
The other issue I see is in the GetScientificFocusAreas() method. On the second line of the code the objects are converted to JSON. Which is fine, but the last line of code the json is passed into the Json() method. Which converts the string into Json yet again. When using the JSON.Net library use the Content() method in the return instead of Json() and set the content type to application/json
The reasoning for using an external Json converter rather than the internal converter is simply the internal json converter has a few known issues. JSON.Net has been around for years and is solid.
I need to use POST to post a string to server and get xml response, the status code is OK but the string reponse is always ="" (0 byte). Is there any thing wrong with my code? I check the server from blackberry, works fine so the problem must come from my code:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
s = NavigationContext.QueryString["parameter1"];
//string strConnectUrl = "http://www.contoso.com/example.aspx";
base.OnNavigatedTo(e);
// DoWebClient(s);
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(strConnectUrl);
httpWebRequest.Method = "POST";
httpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
// start the asynchronous operation
httpWebRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), httpWebRequest);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
// string XML_REQUEST = "<?xml version=\"1.0\"?><mybroker"><getConnections></mybroker>";
string post = "?&track=love";
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
// Convert the string into a byte array.
byte[] postBytes = Encoding.UTF8.GetBytes(post);
// Write to the request stream.
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
static Stream str;
static string st;
private static void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
HttpStatusCode rcode = response.StatusCode;
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
//****THIS ALWAYS RETURN "" VALUE, EXPECT TO RETURN XML STRING****
string responseString = streamRead.ReadToEnd();
//Console.WriteLine(responseString);
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
}
*EDIT**
The code work, however the server return parameter require the Ampersand(&) which is not allow in silverlight framework I think, remove the & char server response but the result wasn't correct. I will ask a new question refer this Ampersand
Have checked your call using Fiddler. The server is returning an empty body. Your code is working correctly. The problem is server side.
The post value should be "track=love".
I tried and it worked but the response is gzip encoded.
Fiddler says that the request is good, and that the response really is blank. If it's working from Blackberry and not from WP7, could the server be doing some user agent checking and not returning anything because it doesn't recognize the WP7 user agent?
Also, it looks like you're POSTing a query string ("?&track=love") which is sort of unusual. Are you sure that's the right data to be sending in your request?