The cornerstone of signed URL security is access control. Think of signing a
getObject operation as a placeholder for sending the file and take extra caution to do that right.
First, check the requesting user so that users who are not supposed to download anything are not able to do so.
Second, make sure to check the requested file so that people can only download files they are supposed to be able to. Just because someone bought a product on your site should not be able to download any product.
Third, make sure to only sign a URL for files that are meant to be downloaded by users and a malicious request can not get one outside a given path or a bucket. If sensitive data is stored in a bucket then make sure in no event a URL can be signed for that.
As with everything related to security, using the least privilege helps to make sure the blast radius of a problem is as small as possible. When you use signed URLs it's best to use a signer entity that has the
s3:GetObject permission for the intended bucket and path and nothing else. See the Using dedicated roles chapter how to implement this.
Increasing the expiration time is tempting as an easy fix to various problems. But that makes signed URLs increasingly prone to interference with other services and is a bad practice security-wise. Short expiration helps with the problem of revocation, as we've seen in the Revocation section, and also to avoid expiration errors due to refreshing temporary credentials, as discussed in ExpiredToken.
If you need to serve private files on a permanent URL, implement that as a backend endpoint. See more at the Permanent URLs section.
As a rule of thumb, use the default 15 minutes expiration.