Deserialization of Untrusted Data - c#

In a security run on our code base we are getting a high priority issue i.e. "Deserialization of Untrusted Data" We are using Newtonsoft JSON package for deserialization. Below is the code snippet used and I followed this stack overflow answer(Fixing the deserializing of untrusted data using C#) to solve this issue. It is still not resolved. Any pointers will be helpful.
var idstate = HttpContext.Current.Request.Form[Constants.State];
var jsonSerializerSettings = new JsonSerializerSettings();
LoginRedirection redirectionObject = JsonConvert.DeserializeObject<LoginRedirectionModel>(idstate, jsonSerializerSettings)?.ToLoginRedirection();
Models used for deserialization are below:-
public class LoginRedirection
{
public string stateUrl { get; set; }
public string cartSession { get; set; }
}
public class LoginRedirectionModel
{
public string stateUrl { get; set; }
public string cartSession { get; set; }
public LoginRedirection ToLoginRedirection()
{
return new LoginRedirection { stateUrl = stateUrl, cartSession = cartSession };
}
}
Security exception "OWASP Top 10 2017: A8-Insecure Deserialization" is coming for the below line
LoginRedirection redirectionObject = JsonConvert.DeserializeObject<LoginRedirectionModel>(idstate, jsonSerializerSettings)?.ToLoginRedirection();
JSON:-
{ "stateUrl"="<URL HERE>", "cartSession":"<GUID HERE>"}
Another aspect to problem is:-
When we consume an API using HttpClient and then trying to deserialize the response from API, we are getting the same security warning. Below is the code for consuming and deserializing the API.
public T Post<T, M>(M data, string url, bool ocpSubscriptionHeaderRequired = true)
{
T response = default(T);
try
{
string postBody = JsonConvert.SerializeObject(data);
using (var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(ApiRequestTimeOutInSeconds) })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (ocpSubscriptionHeaderRequired)
{
client.DefaultRequestHeaders.Remove(Constants.ApiSubscriptionKey);
client.DefaultRequestHeaders.Add(Constants.ApiSubscriptionKey, GenericUtilities.GetConfigData(Constants.ApiSubscriptionKeyValue));
}
HttpResponseMessage result = Task.Run(() => client.PostAsync(url, new StringContent(postBody, Encoding.UTF8, "application/json"))).Result;
if (result.IsSuccessStatusCode)
{
string responseString = Task.Run(() => result.Content.ReadAsStringAsync()).Result;
response = JsonConvert.DeserializeObject<T>(responseString, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.None
});
}
}
}
catch (Exception ex)
{
_logger.WriteException(ex);
}
return response;
}

You can use the JsonConvert.PopulateObject(sourceJsonString, obj) instead of deserializing it using JsonConvert.DesrializeObject<>();.

Sounds like a false-positive to me.
JsonConvert.DeserializeObject with the wrong JsonSerializerSettings options could be used to construct some other .net type, which could execute something unexpected within its constructor. Or you could write your own serialiser, with some other exploitable bugs in it.
But if you can't use JsonConvert.DeserializeObject for a simple object with two string fields, then every .net app that handles json would already be broken.

Related

C# HTTPClient Not Sending Request

I have a WPF application where I need to retrieve configuration data from a remote API. The API has been tested and is working correctly returning what I believe to be the correct format. I have created a data type for the response etc etc but when I get to the line getting the response via the HttpClient it doesn't send any request to the API.
DataType :-
public class Mylist
{
public string ItemName { get; set; }
public string ItemText { get; set; }
public string ConfigName { get; set; }
public string ConfigValue { get; set; }
public string ConfigType { get; set; }
}
Code :-
string licKey = ConfigManager.GetSetting("Lic");
string Uri = ConfigManager.GetSetting("API");
string UserAPI = ConfigManager.GetSetting("Config");
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(Uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(UserAPI + "?Licence=" + licKey);
var data = await response.Content.ReadAsAsync<IEnumerable<Mylist>>();
int count = 0;
List<Mylist> nudges = new List<Mylist>((IEnumerable<Nudgelist>)data);
// Do something with the data
the code builds the correct URL (https://example.com/api/Uri?Licence=licencevalue) for the request and if input manually into the browser it gives a response as per the below :-
<Mylist>
<ConfigName>cName</ConfigName>
<ConfigType>cType</ConfigType>
<ConfigValue>cValue</ConfigValue>
<ItemName>iName</NudgeName>
<ItemText>iText</NudgeText>
</Mylist>
<Mylist>
...
</Mylist>
When I run the code above and step through it then we get to the line "var response = await client.GetAsync(UserAPI + "?Licence=" + licKey);" and it just skips the rest of the code and moves onto the next call, no error raised or failures anywhere to be found.
I have run logs on the API and we are not seeing the request coming in, if we use an identical model of code for calling another API controller to call Uri2 (https://example.com/api/Uri2?Licence=licencevalue) it works fine.
Try this
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<IEnumerable<MyList>>(stringData);
}
else
{
var statusCode= response.StatusCode);
}

How do I retrieve and use data I get with Http GET Requests in c#

So I am trying to use GET requests with c# but nothing is really working out.
I wanted to use the site https://covid19.mathdro.id/api as a test to see if I can get the info out of there and use it in a windows form. But I can't seem to figure out how. The only guides I found weren't really that helpful and it just confused me more. Would anyone be able to help me out?
I have tried to use the HttpClient with JSON.net but I get confused in it.
Been trying for the past 2 hours since I never dealt with HTTP GET Requests in c# other than with Python.
Install the 'Newtonsoft.Json' nuget package.
async Task<JToken> GetREST(string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
return JToken.Parse(jsonData);
}
}
return null;
}
async private void button1_Click(object sender, EventArgs e)
{
var jObj = await GetREST("https://covid19.mathdro.id/api");
var confirmed = jObj["confirmed"];
Console.WriteLine("Confirmed:" + confirmed["value"]);
var confirmedJSON = await GetREST(confirmed["detail"].ToString());
Console.WriteLine(confirmedJSON);
}
In addition to the accepted answer, you can always work with the data as objects by deserializing - I prefer this method over using JToken etc as it tends to be very easy to work with the objects (there's often less boilerplate to pull bits of data from the response).
public async Task<CovidData> GetCovidData(string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<CovidData>(jsonData);
}
}
return null;
}
The objects you'd deserialize to would look like this:
public class CovidData
{
public ValueDetailPair Confirmed { get; set; }
public ValueDetailPair Recovered { get; set; }
public ValueDetailPair Deaths { get; set; }
}
public class ValueDetailPair
{
public int Value { get; set; }
// If you need the link to the detail it would be deserialized to this string member
public string Detail { get; set; }
}
It really does depend on preference and your use case though.
example:
var data = await GetCovidData("https://covid19.mathdro.id/api");
Console.WriteLine(data.Confirmed.Value);
Console.WriteLine(data.Confirmed.Detail);
Console.WriteLine(data.Recovered.Value);

404 error on using graph API from c# console app

I am currently trying to authenticate Graph API using my C#. I am able to query this API and receive the token successfully from Postman.But when I call same API, I get 404 error.
My code is as below:
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace GraphTest
{
public class AuthenticationModel
{
public string grantType { get; set; } = "client_credentials";
public string clientId { get; set; } = "my_ad_app_id";
public string clientSecret { get; set; } = "client_secret";
public string scope { get; set; } = "https://graph.microsoft.com/.default";
}
public class Authentication
{
private static string tenantId = "tenant_id";
private static readonly HttpClient Client = new HttpClient();
public Authentication()
{
var authenticationModel = new AuthenticationModel();
RunAsync().GetAwaiter().GetResult();
}
private static async Task RunAsync()
{
Client.BaseAddress = new Uri("https://login.microsoftonline.com/");
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("*/*"));
Client.DefaultRequestHeaders.Add("Host", "login.microsoftonline.com");
Client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
try
{
var authenticationModel = new AuthenticationModel();
var url = await GetTokenAsync(authenticationModel);
Console.WriteLine($"Created at {url}");
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
}
private static async Task<Uri> GetTokenAsync(AuthenticationModel authenticationModel)
{
var keyValues = authenticationModel.GetType().GetProperties()
.ToList()
.Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")
.ToArray();
var xUrlEncodedBody = string.Join('&', keyValues);
var response = await Client.PostAsJsonAsync(
$"{tenantId}/oauth2/v2.0/token", xUrlEncodedBody);
response.EnsureSuccessStatusCode();
return response;
}
}
}
So, I recieve this in response: StatusCode:404, ReasonPhrase:Not Found
Please help me in knowing that where I am doing it wrong.
Note: API with same data works fine with Postman. Though, I have replaced some values here for security reasons.
You should not post form URL encoded content as JSON (PostAsJsonAsync). The body needs to have a content type of application/x-www-form-urlencoded.
But backing up a second, you don't need to implement the protocol yourself when there are libraries out there that do it for you :). We provide and support the Microsoft Authentication Library (MSAL) which makes this easy.
var app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithTenantId("{tenantID}")
.WithClientSecret(config.ClientSecret)
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
Console.WriteLine($"Error getting token: {ex.Message}");
}
Console.WriteLine($"Token: {result.AccessToken}");
I ran your code and it is wrongly generating query string as below with spaces.
xUrlEncodedBody => grantType = client_credentials&clientId = my_ad_app_id&clientSecret = client_secret&scope = https://graph.microsoft.com/.default
There is space between query parameter name and value, see below line.
.Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")
Remove the space and try again
.Select(p => $"{p.Name}={p.GetValue(authenticationModel)}")

Why do I get "not_authed"-error from Slack when sending a post-request from c#?

I am trying to serialize an object into Json and then send it to Slack. I have done this successfully without serializing but instead using "Dictionary" and "FormUrlEncodedContent" and then send it.
But now, for the purpose of making things easier and more agile, I just wanted to create one JSon-class which I could serialize and then use for every request I want to send.
Here is my code:
My JsonObject:
public class JsonObject
{
private string _token = "xoxp-MyToken";
[JsonProperty("token")]
public string token { get { return _token; } }
[JsonProperty("channel")]
public string channel { get; set; }
[JsonProperty("as_user")]
public bool as_user = true;
[JsonProperty("username")]
public string username { get;set; }
[JsonProperty("text")]
public string text { get; set; }
}
My client:
public class BpsHttpClient
{
private readonly HttpClient _httpClient = new HttpClient { };
public Uri UriMethod { get; set; }
public BpsHttpClient(string webhookUrl)
{
UriMethod = new Uri(webhookUrl);
}
public async Task<HttpResponseMessage> UploadFileAsync(StringContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
}
Main:
class MainArea
{
public static void Main( string[] args)
{
try
{
Task.WaitAll(SendMessage());
}
catch(Exception ass)
{
Console.WriteLine(ass);
Console.ReadKey();
}
}
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/chat.postMessage");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
JO.text = "This is so much fun :D !";
var Json = JsonConvert.SerializeObject(JO, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var StringJson = new StringContent(Json, Encoding.UTF8, "application/json");
var DeSon = JsonConvert.DeserializeObject(Json);
Console.WriteLine(DeSon); //this is for me to see if my JsonObject looks correct - it does ;)
Console.ReadKey();
var Response = await client.UploadFileAsync(StringJson);
string AnswerContent = await Response.Content.ReadAsStringAsync();
Console.WriteLine(AnswerContent);
Console.ReadKey();
}
}
When I run the code I allways get the answer:
Output:
{"ok":false,"error":"not_authed"}
although I think my JsonObject looks right - it has the token in there...
Anybody have an idea why?
So, i figured it out - I SHALL NOT put my token in the JsonObject I want to send.
The solution in this case (using httpclient) is that one has to add a header for authorization to the client, like so:
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "lé token");
and then it works.

Converting the content of HttpResponseMessage to object

My Question: How do I do this?
So, I hadn't touched anything .Net in about 6 years until this week. There's a lot that I've forgotten and even more that I never knew and while I love the idea of the async/await keywords, I'm having a slight problem implementing the following requirements for a client's API implementation:
The ServerAPI class has a method for each of the API methods, taking appropriate input parameters (e.g. the method Login takes in an id and a password, makes the API call and returns the result to the caller).
I want to abstract away the JSON so that my API methods return the actual object you're fetching (e.g. the Login method above returns a User object with your auth token, uid, etc.)
Some API methods return a 204 on success or no meaningful content (not meaningful in my usecase maybe I only care about success/failure), for these I'd like to return either a bool (true = success) or the status code.
I'd like to keep the async/await (or equivalent) design, because it seems to really work well so far.
For some methods, I might need to just return the HttpResponseMessage object and let the caller deal with it.
This is roughly what I have so far and I'm not sure how to make it compliant with the above OR whether I'm even doing this right. Any guidance is appreciated (flaming, however, is not).
// 200 (+User JSON) = success, otherwise APIError JSON
internal async Task<User> Login (string id, string password)
{
LoginPayload payload = new LoginPayload() { LoginId = id, Password = password};
var request = NewRequest(HttpMethod.Post, "login");
JsonPayload<LoginPayload>(payload, ref request);
return await Execute<Account>(request, false);
}
// 204: success, anything else failure
internal async Task<Boolean> LogOut ()
{
return await Execute<Boolean>(NewRequest(HttpMethod.Delete, "login"), true);
}
internal async Task<HttpResponseMessage> GetRawResponse ()
{
return await Execute<HttpResponseMessage>(NewRequest(HttpMethod.Get, "raw/something"), true);
}
internal async Task<Int32> GetMeStatusCode ()
{
return await Execute<Int32>(NewRequest(HttpMethod.Get, "some/intstatus"), true);
}
private async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate)
{
if (authenticate)
AuthenticateRequest(ref request); // add auth token to request
var tcs = new TaskCompletionSource<RESULT>();
var response = await client.SendAsync(request);
// TODO: If the RESULT is just HTTPResponseMessage, the rest is unnecessary
if (response.IsSuccessStatusCode)
{
try
{
// TryParse needs to handle Boolean differently than other types
RESULT result = await TryParse<RESULT>(response);
tcs.SetResult(result);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
else
{
try
{
APIError error = await TryParse<APIError>(response);
tcs.SetException(new APIException(error));
}
catch (Exception e)
{
tcs.SetException(new APIException("Unknown error"));
}
}
return tcs.Task.Result;
}
This is the APIError JSON structure (it's the status code + a custom error code).
{
"status": 404,
"code":216,
"msg":"User not found"
}
I would prefer to stay with System.Net, but that's mostly because I don't want to switch all my code over. If what I want is easier done in other ways then it's obviously worth the extra work.
Thanks.
Here is an example of how I've done it using MVC API 2 as backend. My backend returns a json result if the credentials are correct. UserCredentials class is the exact same model as the json result. You will have to use System.Net.Http.Formatting which can be found in the Microsoft.AspNet.WebApi.Client NugetPackage
public static async Task<UserCredentials> Login(string username, string password)
{
string baseAddress = "127.0.0.1/";
HttpClient client = new HttpClient();
var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", username },
{ "password", password },
};
var Response = await client.PostAsync(baseAddress + "oauth/token", new FormUrlEncodedContent(form));
if (Response.StatusCode == HttpStatusCode.OK)
{
return await Response.Content.ReadAsAsync<UserCredentials>(new[] { new JsonMediaTypeFormatter() });
}
else
{
return null;
}
}
and you also need Newtonsoft.Json package.
public class UserCredentials
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
//more properties...
}
i would use a Deserializer.
HttpResponseMessage response = await client.GetAsync("your http here");
var responseString = await response.Content.ReadAsStringAsync();
[Your Class] object= JsonConvert.DeserializeObject<[Your Class]>(responseString.Body.ToString());
So, first to address the you need Newtonsoft.Json comments, I really haven't felt the need yet. I've found the built in support to work well so far (using the APIError Json in my original question:
[DataContract]
internal class APIError
{
[DataMember (Name = "status")]
public int StatusCode { get; set; }
[DataMember (Name = "code")]
public int ErrorCode { get; set; }
}
I have also defined a JsonHelper class to (de)serialize:
public class JsonHelper
{
public static T fromJson<T> (string json)
{
var bytes = Encoding.Unicode.GetBytes (json);
using (MemoryStream mst = new MemoryStream(bytes))
{
var serializer = new DataContractJsonSerializer (typeof (T));
return (T)serializer.ReadObject (mst);
}
}
public static string toJson (object instance)
{
using (MemoryStream mst = new MemoryStream())
{
var serializer = new DataContractJsonSerializer (instance.GetType());
serializer.WriteObject (mst, instance);
mst.Position = 0;
using (StreamReader r = new StreamReader(mst))
{
return r.ReadToEnd();
}
}
}
}
The above bits I already had working. As for a single method that would handle each request execution based on the type of result expected while it makes it easier to change how I handle things (like errors, etc), it also adds to the complexity and thus readability of my code. I ended up creating separate methods (all variants of the Execute method in the original question:
// execute and return response.StatusCode
private static async Task<HttpStatusCode> ExecuteForStatusCode (HttpRequestMessage request, bool authenticate = true)
// execute and return response without processing
private static async Task<HttpResponseMessage> ExecuteForRawResponse(HttpRequestMessage request, bool authenticate = true)
// execute and return response.IsSuccessStatusCode
private static async Task<Boolean> ExecuteForBoolean (HttpRequestMessage request, bool authenticate = true)
// execute and extract JSON payload from response content and convert to RESULT
private static async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate = true)
I can move the unauthorized responses (which my current code isn't handling right now anyway) into a new method CheckResponse that will (for example) log the user out if a 401 is received.

Categories

Resources