AWS Android SDK not able to access s3 in "sub-directory" - android

I'm using the android sdk generated by AWS API Gateway to get a pre-signed URL for objects in s3 (lambda behind API gateway).
My s3 bucket looks like this:
* module_a
|\
| * file_a
| * subdir_a
| \
| * file_sa
* module_b
This works perfectly for file_a, but for file_sa it doesn't. At least not when I use the android SDK, there I get an URL where the slash is replaced with %25252F.
However, when I test the api in the console, I get the correct URL.
Is there anything I can do with the SDK to fix this?
Update
Here's the chain of code snippets involved in this problem.
Android code to download file (exception happens in last line)
fileName = "css/style.css"; // file in s3
moduleName = "main"; // folder in s3
[...]
ApiClientFactory factory = new ApiClientFactory().credentialsProvider(
aws.credentialsProvider);
apiClient = factory.build(myAPIClient.class);
apiClient.modulesModuleFileGet(fileName.replace("/", "%2F"), moduleName);
URL url = new URL(url_path.getUrl());
URLConnection connection = url.openConnection();
connection.connect();
InputStream in = new BufferedInputStream(connection.getInputStream());
API Gateway
The api endpoint used above is configured with two path parameters (module name and file name). The body mapping template for the call to lambda looks like this:
#set($inputRoot = $input.path('$'))
{
"module" : "$input.params('module')",
"file": "$input.params('file')"
}
Lambda
from __future__ import print_function
import json
import urllib
import boto3
s3 = boto3.client('s3')
def lambda_handler(event, context):
key = event['module'] + "/" + event['file'].replace("%2F", "/")
url = s3.generate_presigned_url(
"get_object",
Params={'Bucket':"mybucket",
'Key': key},
ExpiresIn=60)
return {"url": url}

I've got it to work after following the comments. However I still somehow get double quoted slashes. Here's the working code
Android
public Url modulesModuleFileGet(String fileName, String moduleName) {
try {
String fileNameEnc = URLEncoder.encode(fileName, "UTF-8");
Url ret = getApiClient().modulesModuleFileGet(fileNameEnc, moduleName);
return ret;
} catch (UnsupportedEncodingException e){
Log.e(TAG, "> modulesModuleFileGet(", e);
return null;
}
}
Lambda
def lambda_handler(event, context):
key = event['module'] + "/" + urllib.unquote_plus(urllib.unquote_plus(event['file']))
url = s3.generate_presigned_url(
"get_object",
Params={'Bucket':"my",
'Key': key},
ExpiresIn=60)
return {"url": url}
I'd still welcome further suggestions on how to improve this, but for now it is working. Thanks for the comments pointing me in the right direction.

Related

How to secure webservice URL?

I have an android app with web service urls. If anyone decrypts my apk file, the webservice url will become visible.I am using HTTP POST for calling web service.
Anyone can read the code by decompiling the apk file from this site.
My registration page url got hacked and sending bulk request to this url with post data. I was using a API_KEY and send the API_KEY with post data. API_KEY was stored in gradle.properties file.
I did not used
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'` when its got hacked.
After some search, i know that there is no 100% secure methods to hide url.
My code for registration is :
String link = "http://xxxxxxxxxx.php";
String data = URLEncoder.encode("name", "UTF-8") + "=" + URLEncoder.encode(name, "UTF-8");
data += "&" + URLEncoder.encode("phone", "UTF-8") + "=" + URLEncoder.encode(phone, "UTF-8");
data += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8");
URL url = new URL(link);
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(data);
wr.flush();
I don't know if it is the correct method to post data to a Url.
How can i secure my source code?
Can i store all my web service url in server?
I am beginner to android. Please help!
I use it this in all android application I developed
gradle.properties
API = http://ec2xxxxx.compute.amazonaws.com
API_KEY = $2c11SoL/NjJ28
create utils.gradle
utils.gradle
class Utils {
static def r = new Random(System.currentTimeMillis())
}
def String toJavaCodeString(String string) {
byte[] b = string.getBytes();
int c = b.length;
StringBuilder sb = new StringBuilder();
sb.append("(new Object() {");
sb.append("int t;");
sb.append("public String toString() {");
sb.append("byte[] buf = new byte[");
sb.append(c);
sb.append("];");
for (int i = 0; i < c; ++i) {
int t = Utils.r.nextInt();
int f = Utils.r.nextInt(24) + 1;
t = (t & ~(0xff << f)) | (b[i] << f);
sb.append("t = ");
sb.append(t);
sb.append(";");
sb.append("buf[");
sb.append(i);
sb.append("] = (byte) (t >>> ");
sb.append(f);
sb.append(");");
}
sb.append("return new String(buf);");
sb.append("}}.toString())");
return sb.toString();}
ext.toJavaCodeString = this.&toJavaCodeString
build.gradle
apply from: "utils.gradle"
android {
defaultConfig {
buildConfigField 'String', 'API', toJavaCodeString(API)
buildConfigField 'String', 'API_KEY', toJavaCodeString(API_KEY)
}}
and access your private url;
public static final String API = BuildConfig.API;
There is no way to protect "secret" information such as URLs embedded in an APK. A determined / motivated hacker can defeat any scheme you care to design ... if he / she has access to a platform where your app is being run.
In order for your app to run, the app running on the user's device needs to be able to decrypt the hidden URL. The user can either intercept the URL in decrypted form in the app's address space, or he / she can reverse engineer the algorithm and decryption key you are using to do the decryption.
Another "attack" is that your app needs to use the URL to make a request. That request can be intercepted on the users device before it is protected by the SSL / TLS channel to your (presumably) HTTPS enabled service.
And on top of that, if you embed a "secret" URL into an app and that secret is compromised and you have to turn off / relocate your server, then you are making problems for all (legitimate, paying, etc) users of your app. They won't be happy campers.
The correct approach is to make your service secure ... and use some kind of authentication mechanism so that hackers need more than just the URL to make requests. Users can / should be issued with individual credentials (e.g. auth keys), and you need to implement a way to invalidate a given users' credentials at the server end.
"URL" stands for Universal Resource Locator. The whole point of a URL is to access some kind of resource, and to do this, you obviously have to tell the network you are connecting to what it is you want to fetch.
Now if you are worried that a hacker can access your source code, then surely he can also hook up Wireshark or Fiddler and simply observe what connections your app is making, and what info you are passing along and receiving back.
Sorry, but I simply can't see any good way around this.
If you're worried about your server/services being hacked or DOS`ed, I think you had better focus on securing them as well as you can, rather than trying to hide them.
I have fixed this in two ways
Encrypted the URL in my code with my private key and on request call i decrypted it again,
public static String encryptIt(String value) {
try {
DESKeySpec keySpec = new DESKeySpec(new byte[]{105, 107, 18, 51, 114, 83, 51, 120, 121});
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
byte[] clearText = value.getBytes("UTF8");
// Cipher is not thread safe
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
// Log.d("aa", "Encrypted: " + value + " -> " + encrypedValue);
return Base64.encodeToString(cipher.doFinal(clearText), Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
and decript it by using this
public static String decryptIt(String value) {
try {
DESKeySpec keySpec = new DESKeySpec(new byte[]{105, 107, 18, 51, 114, 83, 51, 120, 121});//cryptoPass.getBytes("UTF8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
byte[] encrypedPwdBytes = Base64.decode(value, Base64.DEFAULT);
// cipher is not thread safe
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));
// Log.d("aa", "Decrypted: " + value + " -> " + decrypedValue);
return new String(decrypedValueBytes);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
note in my case that the private key is new byte[]{105, 107, 18, 51, 114, 83, 51, 120, 121} i think it was $ecrEt or something like i forget it.
so if they decompile the APK they wan't be able to find the service link inside you code.
so the base url will be like this public static final String ROOT_API = "aHR0cHSC86LSy9tbS2JpuZW50aWtoYWAbGUJhdC5qbw==";
2- Also you have to add progaurd to your code
BUT, they can smurfing the netweok and find the url if the hacker is advance person, in this case you have to user SSl certificate "https" and make the webserivce POST.
hope you got my point.

Using Google Cloud Storage JSON api in android

I want to upload image on Google Cloud Storage from my android app. For that I searched and found that GCS JSON Api provides this feature. I did a lot of research for Android sample which demonstrates its use. On the developer site they have provided code example that only support java. I don't know how to use that API in Android. I referred this and this links but couldn't get much idea. Please guide me on how i can use this api with android app.
Ok guys so I solved it and got my images being uploaded in Cloud Storage all good.
This is how:
Note: I used the XML API it is pretty much the same.
First, you will need to download a lot of libraries.
The easiest way to do this is create a maven project and let it download all the dependencies required. From this sample project :
Sample Project
The libraries should be:
Second, you must be familiar with Cloud Storage using the api console
You must create a project, create a bucket, give the bucket permissions, etc.
You can find more details about that here
Third, once you have all those things ready it is time to start coding.
Lets say we want to upload an image:
Cloud storage works with OAuth, that means you must be an authenticated user to use the API. For that the best way is to authorize using Service Accounts. Dont worry about it, the only thing you need to do is in the API console get a service account like this:
We will use this service account on our code.
Fourth, lets write some code, lets say upload an image to cloud storage.
For this code to work you must put your key generated in step 3 in assets folder, i named it "key.p12".
I don't recommend you to do this on your production version, since you will be giving out your key.
try{
httpTransport= new com.google.api.client.http.javanet.NetHttpTransport();
//agarro la key y la convierto en un file
AssetManager am = context.getAssets();
InputStream inputStream = am.open("key.p12"); //you should not put the key in assets in prod version.
//convert key into class File. from inputstream to file. in an aux class.
File file = UserProfileImageUploadHelper.createFileFromInputStream(inputStream,context);
//Google Credentianls
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
.setServiceAccountPrivateKeyFromP12File(file)
.build();
String URI = "https://storage.googleapis.com/" + BUCKET_NAME+"/"+imagename+".jpg";
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
//byte array holds the data, in this case the image i want to upload in bytes.
HttpContent contentsend = new ByteArrayContent("image/jpeg", byteArray );
HttpRequest putRequest = requestFactory.buildPutRequest(url, contentsend);
com.google.api.client.http.HttpResponse response = putRequest.execute();
String content = response.parseAsString();
Log.d("debug", "response is:"+response.getStatusCode());
Log.d("debug", "response content is:"+content);} catch (Exception e) Log.d("debug", "Error in user profile image uploading", e);}
This will upload the image to your cloud bucket.
For more info on the api check this link Cloud XML API
Firstly, You should get the below information by registering your application in the GCP console.
private final String pkcsFile = "xxx.json";//private key file
private final String bucketName = "your_gcp_bucket_name";
private final String projectId = "your_gcp_project_id";
Once you get the credentials, you should put the private key (.p12 or .json) in your assets folder. I'm using JSON format private key file. Also, you should update the image location to upload.
#RequiresApi(api = Build.VERSION_CODES.O)
public void uploadImageFile(String srcFileName, String newName) {
Storage storage = getStorage();
File file = new File(srcFileName);//Your image loaction
byte[] fileContent;
try {
fileContent = Files.readAllBytes(file.toPath());
} catch (IOException e) {
e.printStackTrace();
return;
}
if (fileContent == null || fileContent.length == 0)
return;
BlobInfo.Builder newBuilder = Blob.newBuilder(BucketInfo.of(bucketName), newName);
BlobInfo blobInfo = newBuilder.setContentType("image/png").build();
Blob blob = storage.create(blobInfo, fileContent);
String bucket = blob.getBucket();
String contentType = blob.getContentType();
Log.e("TAG", "Upload File: " + contentType);
Log.e("File ", srcFileName + " uploaded to bucket " + bucket + " as " + newName);
}
private Storage getStorage() {
InputStream credentialsStream;
Credentials credentials;
try {
credentialsStream = mContext.getAssets().open(pkcsFile);
credentials = GoogleCredentials.fromStream(credentialsStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return StorageOptions.newBuilder()
.setProjectId(projectId).setCredentials(credentials)
.build().getService();
}

Problems with asp HttpResponse on a download from Android

I have the following problem:
I develop a .zip file download with .pdf selected inside a function on c#.
The function creates the .zip file on a temp folder on the server and the HTTP Response return this file.
This function works great when is called from windows, and IOS. But when I call this function on Android it never downloads the file.
On the server I see that the function create the .zip file again and again when it's called from android browser (chrome, dolphin...) and it's never returned.
The strange thing is that I could run it well when I selected 90 .pdf files (although the function is called twice for no reason), but when I select 140 (or more) the issue happens again.
Here is the code:
**string dirName = Utiles.GetNewName();
zipName += ".ZIP\"";
string urlRet = _mSTempPath + #"\" + dirName + ".ZIP";
string urlDelete = _mSTempPath + #"\" + dirName;
System.IO.Stream iStream = null;
// Total bytes to read:
long dataToRead;
//HttpResponse resp = _page.Response;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
using (ZipFile zip = new ZipFile())
{
foreach (string url in filesURLs)
{
zip.AddFile(url,"");
}
zip.Save(_mSTempPath + #"\" + dirName + ".ZIP");
}
// Open the file.
iStream = new System.IO.FileStream(urlRet, System.IO.FileMode.Open,
System.IO.FileAccess.Read, System.IO.FileShare.Read);
// Total bytes to read:
dataToRead = iStream.Length;
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=\"" + zipName);
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader("content-length", dataToRead.ToString());
HttpContext.Current.Response.BufferOutput = true;
HttpContext.Current.Response.TransmitFile(urlRet);
HttpContext.Current.Response.Flush();**
Please, I will be very grateful if anyone can help.
Thanks again!

pass two parameters with URL link in android

i have problem with two parameters passing with URL link. Can anyone help me?
private void FillDetails(String _userid,int _sporttype) {
al_TeamName=new ArrayList<String>();
try{
spf=SAXParserFactory.newInstance();
sp=spf.newSAXParser();
xr=sp.getXMLReader();
URL sourceUrl = new URL(
"http://10.0.2.2:2291/acd.asmx/Get_Teams?_userid ="+_userid & "_sporttype="+ _sporttype);
MyHandler mh=new MyHandler();
xr.setContentHandler(mh);
xr.parse(new InputSource(sourceUrl.openStream()));
setListAdapter(new MyAdapter());
}
catch(Exception ex)
{
}
}
when i using this code, i am getting null.If i send single parameter then it works fine.
Is this correct procedure for URL passing two parameters?
Thanks in advance..........
UPDATED ANSWER:
Now you have multiple errors in your URL:
URL sourceUrl = new URL("http://10.0.2.2:2291/acd.asmx/Get_Teams?_userid =" +
_userid & "_sporttype="+ _sporttype);
You still have a space before the first = sign
There's no + between the _userid variable and the rest of the string.
The & sign is outside the second string
It should be something like this:
URL sourceUrl = new URL("http://10.0.2.2:2291/acd.asmx/Get_Teams?_userid="
+ _userid + "&_sporttype=" + _sporttype);
ORIGINAL ANSWER:
You currently have a space instead of a = sign after your first parameter:
?_userid "+_userid
should be
?_userid="+_userid
Solved.
URL sourceUrl = new URL("http://0.0.0.0/acd.asmx/GetList?Value1="+Value1+"&ID="+ID);
"http://10.0.2.2:2291/acd.asmx/Get_Teams?_userid ="+_userid & "_sporttype="+ _sporttype);
You have an & after _userid, which probably does who knows what on _userid. Usually a single & does binary manipulation, so you might be transforming what comes out of _userid. Also, I would recommend URLEncoding your REST tags if you aren't doing that already
I would recommend logging the REST parameters while in development as well to double-check that it's being formed correctly
Update: The & was outside the quote and you needed to use a +
"http://10.0.2.2:2291/acd.asmx/Get_Teams?_userid ="+_userid + "&_sporttype="+ _sporttype);
If you came here because you searched for a version working in Kotlin (like me), you can use this function to build your URL:
import java.net.URL
// Your URL you want to append the query on
val url: String = "http://10.0.2.2:2291/acd.asmx/Get_Teams"
// The parameters you want to pass
val params: Map<String, String> = mapOf(
"_userid" to _user_id
, "_sporttype" to _sporttype
)
// The final build url. Standard encoding for URL is already utf-8
val final_url: URL = URL(
"$url?" // Don't forget the question-mark!
+ params.map {
"${it.key}=${it.value}"
}.joinToString("&")
)

Android: Simple way to upload a picture to an image host

I d like to upload a picture to a host like: http://imagerz.com/ .
Any sample for this? I can do a simple POST request, but how I can add the image content to my POST request?
Here is a tutorial that tells you how to send a file via FTP to a server. File upload and download using Java
It shouldn't be very hard to "port" that code into android. (You may have to change some of the classes/methods as some of them may not be implemented in Android's lightweight VM).
There are also other image hosting services that should have an api that you could follow.
EDIT:
As you stated, you wanted to do this with a post request.
I found this great tutorial with the following code:
package com.commonsbook.chap9;
import java.io.File;
import java.io.IOException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.MultipartPostMethod;
public class HttpMultiPartFileUpload {
private static String url =
"http://localhost:8080/HttpServerSideApp/ProcessFileUpload.jsp";
public static void main(String[] args) throws IOException {
HttpClient client = new HttpClient();
MultipartPostMethod mPost = new MultipartPostMethod(url);
client.setConnectionTimeout(8000);
// Send any XML file as the body of the POST request
File f1 = new File("students.xml");
File f2 = new File("academy.xml");
File f3 = new File("academyRules.xml");
System.out.println("File1 Length = " + f1.length());
System.out.println("File2 Length = " + f2.length());
System.out.println("File3 Length = " + f3.length());
mPost.addParameter(f1.getName(), f1);
mPost.addParameter(f2.getName(), f2);
mPost.addParameter(f3.getName(), f3);
int statusCode1 = client.executeMethod(mPost);
System.out.println("statusLine>>>" + mPost.getStatusLine());
mPost.releaseConnection();
}
}
Source: http://www.theserverside.com/news/1365153/HttpClient-and-FileUpload
The same issues about porting this code to android as I stated above apply.

Categories

Resources