I have a Web Application with Angular as front end and Spring Boot webservices as the backend. i am able to perform "Web Parameter Tampering" attack on one of my post call which inserts data in to my database. Has anyone solved this before ?
Appreciate your help.
Angular :
$scope.submitRequest = function() {
ar.createRequest.save({
orderNum : orderNum
...
}, function() {
}, function (httpResponse){
});
};
SpringBoot :
#RequestMapping(value = "/v1/...", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#CrossOrigin
public ResponseEntity<?> createRequest(#RequestBody OrderDTO orderdto){
RestTemplate restTemplate = new RestTemplate();
.......
.....
return new ResponseEntity<>(HttpStatus.CREATED);
}
Related
I can't make heads or tails of what I'm doing wrong here. I'm calling a new controller I'm setting up using JavaScript fetch(). I'm using MVC 5 within the React template.
The template controller (WeatherForecastController) works, if I call it using fetch, it'll hit the controller. Yet using the exact same implementation but for a new controller it won't touch the new controller. However Network debugger shows that it called and returned OK. If I call the controller directly through the browser, it's as if there isn't a route for it at all! No json is returned from my new controller vs the template controller. I'm simply trying to define a new controller I can fetch data from my Middle tier (.NET 6). Using .Net 6.0 as well.
Naming convention is correct as it ends with Controller in call.
DEFAULT TEMPLATE CONTROLLER CALL FROM JS
async populateWeatherData() {
const response = await fetch('weatherforecast');
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
DEFAULT TEMPLATE CONTROLLER
using Microsoft.AspNetCore.Mvc;
namespace ReactTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
Yet I call my controller I setup with the exact same implementation. If I try to call it I get exactly nothing!
NEW CONTROLLER TEMPLATE CALL FROM JS
async populateWeatherData() {
const response = await fetch('database');
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
IMPLEMENTATION OF NEW CONTROLLER (That I actually want to call)
using Microsoft.AspNetCore.Mvc;
namespace ReactTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class DatabaseController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<DatabaseController> _logger;
public DatabaseController(ILogger<DatabaseController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
I've tried everything but modifying the default routing (the controller lives in the same folder as the template controller, so this shouldn't be why).
Before anyone asks:
ROUTING CONFIG:
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
ProjectFileDirectory
DatabaseControllerResponseHeader
So what exactly am I doing wrong? Yes I have read the documentation, no it hasn't helped.
Thanks in advance!
In my main.js file I want to have data from spring boot controller in some specific json format.
eg.
var contactsJSON = [{"userId": "firstuser", "displayName": "firstuser"},
{"userId": "seconduser", "displayName": "seconduser"}];
Now in my controller "/users" i'm returning list of all users.
I want that at the time of application loading the value of contactsJSON gets populated dynamically in required json format (I only need username to create JSON).
main.js
var contactsJSON = [{"userId": "firstuser", "displayName": "firstuser"
},
{"userId": "seconduser", "displayName": "seconduser"
},
{"userId": "thirduser", "displayName": "thirduser"
}
];
UserController.java
#RequestMapping(value = "/users", method = RequestMethod.GET)
public String viewUsers(Model model) {
List<User> list = userService.getAllUsers();
model.addAttribute("userList", list);
return "welcome";
}
List contains private Long id;
private String username;
private String password;
private String passwordConfirm;
I want to dynamically provide value of contactsJSON in my javascript file. How can I do this ?
You can either return a response as String or you can use ResponseEntity Object provided by Spring as below. By this way, you can also return Http status code which is more helpful in the web service call.
#RestController
#RequestMapping("/api")
public class MyRestController
{
#RequestMapping(value = "/users", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> viewUsers(Model model) {
{
//Get data from service layer into entityList.
List<User> list = userService.getAllUsers();
List<JSONObject> entities = new ArrayList<JSONObject>();
for (Entity n : list) { // You can iterate over the list and add in json format below is the example for same
//JSONObject entity = new JSONObject();
//entity.put("aa", "bb");
//entities.add(entity);
}
return new ResponseEntity<Object>(entities, HttpStatus.OK);
}
}
First, a Thymeleaf recommendation
I highly recommend Thymeleaf over JSP templates. For one thing, it makes inline object-to-JSON expressions very easy. For example...
<script th:inline="javascript">
const contactsJSON = [[${userList}]];
</script>
See https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#advanced-inlined-evaluation-and-javascript-serialization
If you don't want to switch, I would recommend adding an AJAX call to fetch your user data. On the Java side, it might look like this
#GetRequest(path = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<User> getAllUsers() {
return userService.getAllUsers();
}
Spring will automatically serialize the response as JSON.
Then in your welcome.jsp
<script>
// load the current URL (ie /users) but requesting JSON
fetch('', {
headers: {
Accept: 'application/json'
}
}).then(res => res.json()).then(contactsJSON => {
// you can now use contactsJSON here
})
</script>
Let me give an example of my problem,
I have registered my routes as following(RouteConfig.cs):
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
if you look at my controller, it has the following function;
[Route("all")]
public HttpResponseMessage Get(HttpRequestMessage request)
{
return CreateHttpResponse(request, () =>
{
HttpResponseMessage response = null;
var HolidayCalendars = _holidayCalendarsRepository.GetAll().ToList();
IEnumerable<HolidayCalendarViewModel> holiVm = Mapper.Map<IEnumerable<HolidayCalendar>, IEnumerable<HolidayCalendarViewModel>>(HolidayCalendars);
response = request.CreateResponse<IEnumerable<HolidayCalendarViewModel>>(HttpStatusCode.OK, holiVm);
return response;
});
}
up to this point everything is going great. My page loads with the requested data. Now, when I go and add another function, for example;
[Route("allHolidays/{id:int}")]
public HttpResponseMessage GetHolidays(HttpRequestMessage request, int id)
{
return CreateHttpResponse(request, () =>
{
HttpResponseMessage response = null;
HolidayCalendar Calendar = _holidayCalendarsRepository.GetSingle(id);
var Holidays = Calendar.Holidays.OrderBy(s => s.HolidayDate).ToList();
IEnumerable<HolidayViewModel> holidayVm = Mapper.Map<IEnumerable<Holiday>, IEnumerable<HolidayViewModel>>(Holidays);
response = request.CreateResponse<IEnumerable<HolidayViewModel>>(HttpStatusCode.OK, holidayVm);
return response;
});
}
I will get the following error in my webpage;
Failed to load resource: the server responded with a status of 400 (Bad Request)
Strange thing is, my request did not change, there is only a new Controller in my api.
This should not be happening because my code is requesting different routes, for example;
function loadData() {
apiService.get('/api/HolidayCalendars/all', null,
HolidayCalendarLoadCompleted,
HolidayCalendarLoadFailed);
}
or
function loadData() {
apiService.get('/api/HolidayCalendars/allHolidays?id=' + $routeParams.id, null,
HolidaysLoadCompleted,
HolidaysLoadFailed);
}
Any ideas?
constructor class WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional }
);
}
Your route for allHolidays implies this format
/api/HolidayCalendars/allHolidays/123
According to your route attribute
[Route("allHolidays/{id:int}")]
But you've passed the id as a querystring parameter
api/HolidayCalendars/allHolidays?id=123
Looks like you're using AttributeRouting (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2) on the Controllers :
[Route("all")]
but you're using standard routing in the config:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Try activate the AttributeRouting with:
configuration.MapHttpAttributeRoutes();
where configuration is the actual instance of HttpConfiguration.
The issue is in your WebApiConfig. In the routeTemplate, you haven't specified an action.
routeTemplate: "api/{controller}/{id}",
If I remember correctly, this is the default config for WebAPI. It filters requests on a controller by verb. That's why when you call
apiService.get('/api/HolidayCalendars/all'.....)
It returns the Get() method on your HolidayCalendars controller.
To fix the issue, add the {action} parameter to your routeTemplate:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional }
);
}
I finally found the solution!
At the top of my code i had a reference to System.Web.Mvc. This way the routing and RESTful functions were not interpreted as it should in a web Api. This caused some strange functioning in my app.
Solution:
Change
using System.Web.Mvc;
To
using System.Web.Http;
This evaded me for three days,until I came along the following answer:
https://stackoverflow.com/a/21999235/6033193
I'm a very new comer to the asp.net web api world. I've got the basic understanding of get(), put(), post() and delete.
In my application, I require two more get() method. An explanation is given below-
public class StudentController : ApiController
{
public IEnumerable Get()
{
//returns all students.
}
//I would like to add this method=======================
[HttpGet]
public IEnumerable GetClassSpecificStudents(string classId)
{
//want to return all students from an specific class.
}
//I also would like to add this method=======================
[HttpGet]
public IEnumerable GetSectionSpecificStudents(string sectionId)
{
//want to return all students from an specific section.
}
public Student Get(string id)
{
//returns specific student.
}
}
There is already a $http.get(..) in angularjs controller.
My question is, how can I call the two additional get() methods from angular controller.
Well, I haven't used asp.net mvc in forever. But you be able to do something like:
public class StudentController : ApiController
{
[Route("students")]
public IEnumerable Get()
{
//returns all students.
}
//I would like to add this method=======================
[HttpGet]
[Route("students/class/{classId}")]
public IEnumerable GetClassSpecificStudents(string classId)
{
//want to return all students from an specific class.
}
//I also would like to add this method=======================
[HttpGet]
[Route("students/section/{sectionId}")]
public IEnumerable GetSectionSpecificStudents(string sectionId)
{
//want to return all students from an specific section.
}
[Route("students/{id}")]
public Student Get(string id)
{
//returns specific student.
}
}
You could also specify routes in the routeconfig like this:
routes.MapRoute(
name: "students",
url: "students/class/{classId}",
defaults: new { controller = "Student", action = "GetClassSpecificStudents", id = UrlParameter.Optional }
);
You have to try for your self. And you can read more about it here and here.
Not that you have your specified routes you can add angular $http.gets for each route.
var url = "whateverdoma.in/students/"
$http.get(url)
.success()
.error()
var url = "whateverdoma.in/students/class/" + classId;
$http.get(url)
.success()
.error()
var url = "whateverdoma.in/students/filter/" + filterId;
$http.get(url)
.success()
.error()
What you want to do is write costum angular resource method, to call your API.
Use angular $resource and not $http - > it is the more common usage (and more REST oriented: $resource wraps $http for use in RESTful web API scenarios).
Read about it
Find how to add a resource to the $resource service.
Here is an example:
.factory('Store', function ($resource, hostUrl) {
var url = hostUrl + '/api/v3/store/';
return $resource("", { storeId: '#storeId' }, {
getSpecific: {
method: 'GET',
url: hostUrl + '/api/v3/store-specific/:storeId'
}
});
})
I am creating a REST API and I have been playing with the idea of allowing bundling of requests from clients. By bundling I mean they can send one request, containing multiple "real" requests, and they get delivered to the client together. Typically javascript ajax requests. Something like this:
POST /bundlerequest
["/person/3243", "/person/3243/friends", "/comments/3243?pagesize=10&page=1", "/products", "/product/categories" ]
(The bundled request can only be GET requests, as of now at least)
This is intended to return something like this
{
"success" : ["/person/3243", "/person/3243/friends", "/comments/3243?pagesize=10&page=1", "/products", "/product/categories" ],
"error" : [],
"completiontime" : 94,
other relevant metadata...
"responses" : [
{"key" : "/person/3243" , "data" : {"name" : "John", ...} },
{"key" : "/person/3243/friends" , "data" : [{"name": "Peter", "commonfriends" : 5, ...}] },
etc...
]
}
The benefits of this bundling is that it reduces the number of requests and that is especially important on mobile devices for instance.
So my first question is, is my approach to this a good one? Does anyone have experience with doing something like this?
AFAIK the common way of solving this is to write server side code to return combined data, that I believe is relevant for the client(s). (The twitter user stream for instance does this, combining person info, latest tweets, latest personal messages etc.) But this makes the API very opinionated and when the client needs changes the server might need to change to accomodate to optimize.
And the second question is how to implement this?
My backend is ASP.NET MVC 3 and IIS 7. Should I implement it in the application, having an bundlerequest action that internally calls the other actions specified in the request?
Could it be implemented in IIS 7 directly? Writing a module that transparently intercepts requests to /bundlerequest and then calls all the corresponding sub requests, making the application totally unaware of the bundling that happens? This would also allow me to implement this in an application-agnostic way.
You could use an asynchronous controller to aggregate those requests on the server. Let's first start by defining a view model that will be returned by the controller:
public class BundleRequest
{
public string[] Urls { get; set; }
}
public class BundleResponse
{
public IList<string> Success { get; set; }
public IList<string> Error { get; set; }
public IList<Response> Responses { get; set; }
}
public class Response
{
public string Key { get; set; }
public object Data { get; set; }
}
then the controller:
public class BundleController : AsyncController
{
public void IndexAsync(BundleRequest request)
{
AsyncManager.OutstandingOperations.Increment();
var tasks = request.Urls.Select(url =>
{
var r = WebRequest.Create(url);
return Task.Factory.FromAsync<WebResponse>(r.BeginGetResponse, r.EndGetResponse, url);
}).ToArray();
Task.Factory.ContinueWhenAll(tasks, completedTasks =>
{
var bundleResponse = new BundleResponse
{
Success = new List<string>(),
Error = new List<string>(),
Responses = new List<Response>()
};
foreach (var task in completedTasks)
{
var url = task.AsyncState as string;
if (task.Exception == null)
{
using (var response = task.Result)
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
bundleResponse.Success.Add(url);
bundleResponse.Responses.Add(new Response
{
Key = url,
Data = new JavaScriptSerializer().DeserializeObject(reader.ReadToEnd())
});
}
}
else
{
bundleResponse.Error.Add(url);
}
}
AsyncManager.Parameters["response"] = bundleResponse;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult IndexCompleted(BundleResponse response)
{
return Json(response, JsonRequestBehavior.AllowGet);
}
}
and now we can invoke it:
var urls = [
'#Url.Action("index", "person", new { id = 3243 }, Request.Url.Scheme, Request.Url.Host)',
'#Url.Action("friends", "person", new { id = 3243 }, Request.Url.Scheme, Request.Url.Host)',
'#Url.Action("index", "comments", new { id = 3243, pagesize = 10, page = 1 }, Request.Url.Scheme, Request.Url.Host)',
'#Url.Action("index", "products", null, Request.Url.Scheme, Request.Url.Host)',
'#Url.Action("categories", "product", null, Request.Url.Scheme, Request.Url.Host)'
];
$.ajax({
url: '#Url.Action("Index", "Bundle")',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(urls),
success: function(bundleResponse) {
// TODO: do something with the response
}
});
Of course some tweaking might be necessary to adapt this to your specific needs. For example you mentioned sending AJAX requests with session expired which might redirect to the Logon page and thus not capturing the error. That's indeed a PITA in ASP.NET. Phil Haack blogged a possible way to circumvent this undesired behavior in a RESTful manner. You just need to add a custom header to the requests.
I suggest looking wcf web api