Viewed   67 times

I use my PHP back-end to detect AJAX requests by checking for a value in $_SERVER['HTTP_X_REQUESTED_WITH'].

This gives me a reliable detection, making sure the request is made utilizing AJAX techniques.

How can I make sure the request came from my own domain, and not an external domain/robot?

www.example.com/ajax?true could allow anyone to make an AJAX call and cut the information.

I could make sessions for everyone that enters my website normally, and then allow AJAX calls.. but that can be faked too.

Does it even matter these days?

 Answers

1

Let you Controller

  • generate access token
  • store in session for later comparison

In your View

  • declare the access token as JS variable
  • send the token with each request

Back in your Controller

  • validate HTTP_X_REQUESTED_WITH
  • validate token

Check these security guidelines from OpenAjax.
Also, read the article on codinghorror.com Annie linked.

Tuesday, October 18, 2022
2

I think you're looking for a variation of Single Sign On. This is a technique in which an authentication in one site is recognised transparently in another. Here is how it works in your case.

Normally you would have a link in site2.com like this:

http://site1.com/login.php?pin=123456789

However, site1.com cannot tell from the referrer which site it has really come from, since it it can be trivially faked. Of course, that may not matter for your use case, if you only want a simple level of security. But if you want something better, read on!

You can use a hashing system and a shared secret, to create something that can only have come from one source. Both sites have the same shared secret, stored in a file. We'll call that $sharedSecret. The algorithm goes like this:

$hash = hashFunction($pin . $sharedSecret);

Then you can do this in site2.com:

<a
    href="http://site1.com/login.php?pin=<?php echo (int) $pin ?>&amp;hash=<?php echo $hash ?>"
    alt="Authenticated link"
>

When site1.com sees it, it can get the PIN straight away, repeat the algorithm, and check that the hash really did come from site2.com. If you have several referring sites, then site1.com should store a separate secret for all of them, and then it can securely check the referrer to see which one it should load.

The shared secret should be substantial enough that it cannot be guessed; I tend to go for around 40-60 characters.

However, the remaining flaw in this plan is that someone could visit site2.com and steal a link from them, and it would still work, providing they were also willing to fake the referrer every time they wanted access. It may therefore be useful to add a timestamp into the algorithm too:

// The time is rounded to the nearest 500 seconds, to account for
// out of sync clocks. Adjust this depending on how long you want links to
// remain active for
$time = floor(time() / 500) * 500;
$hash = hashFunction($pin . $sharedSecret . $time);

Then on site1.com you should compute two hashes:

  • One for floor(time() / 500) * 500
  • One for floor(time() / 500) * 500 - 500

If the supplied hash matches either, allow the link to unlock the content. This accounts for the possibility that the time went over a +/-500 boundary between one server and the next.

I haven't mentioned a specific hashing function here. SHA256 should be fine, but note I'm not a cryptographer. If you want more security again, it may be worth checking to ensure someone isn't brute-forcing the system by flooding your system with guesses - though over the internet it is hardly worth their trying.

Monday, August 29, 2022
 
ewwhite
 
5

you can try to use StreamingResponseBody

StreamingResponseBody

A controller method return value type for asynchronous request processing where the application can write directly to the response OutputStream without holding up the Servlet container thread.

Because you are working on a separate thread, writing directly to the response, your problem to call close() before return is solved.

probably you can start by the following example

public ResponseEntity<StreamingResponseBody> export(...) throws FileNotFoundException {
    //...

    InputStream inputStream = new FileInputStream(new File("/path/to/example/file"));


    StreamingResponseBody responseBody = outputStream -> {

        int numberOfBytesToWrite;
        byte[] data = new byte[1024];
        while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
            System.out.println("Writing some bytes..");
            outputStream.write(data, 0, numberOfBytesToWrite);
        }

        inputStream.close();
    };

    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.bin")
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(responseBody);
}

You can also try to use Files (since java 7)

so you don't have to manage InputStream

    File file = new File("/path/to/example/file");

    StreamingResponseBody responseBody = outputStream -> {
        Files.copy(file.toPath(), outputStream);
    };

As @Stackee007 described in comment, under heavy load in production environment it's a good practice also to define a @Configuration class for a TaskExecutor to tune parameters and manage Async processes.

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {

    private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);

    private final TaskExecutionProperties taskExecutionProperties;

    public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
        this.taskExecutionProperties = taskExecutionProperties;
    }

    //  ---------------> Tune parameters here
    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        log.debug("Creating Async Task Executor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
        executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
        executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
        executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
        return executor;
    }
    
    //  ---------------> Use this task executor also for async rest methods
    @Bean
    protected WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setTaskExecutor(getTaskExecutor());
            }
        };
    }

    @Bean
    protected ConcurrentTaskExecutor getTaskExecutor() {
        return new ConcurrentTaskExecutor(this.getAsyncExecutor());
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

How to test with mockMvc

You can simply follow this sample code in your integration test as:

    .andExpect(request().asyncStarted())
    .andDo(MvcResult::getAsyncResult)
    .andExpect(status().isOk()).getResponse().getContentAsByteArray();

Content type of ResponseEntity<StreamingResponseBody> is a MediaType.APPLICATION_OCTET_STREAM in this example and you can get byte[] (.getContentAsByteArray()) but you can get String/Json/plaintext of everything depending of your body response content type.

Sunday, September 25, 2022
 
5

You need to authenticate the user somehow.

Your user needs to be authenticated with a username and a password.

PHP session can be used to remember, and you should use a database table or a text file on the server to store file ownership information.

Then, before unlinking anything, your logic should make sure that the currently "authenticated" user is the owner of the file.

Tuesday, September 27, 2022
 
4

An Ajax call is exactly identical to any other HTTP request that you make except that it's asynchronous (it doesn't reload the web browser). So you should be using whatever authentication you currently employ on your web site.

This could either be Windows integrated security, cookies, etc. Basically your PHP script just has to verify that the request is coming from a valid user of your application.

Tuesday, September 6, 2022
 
mei-lin
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :